文件名为mymsg.c参考kmsg.c,printk.c
目的:想定义1个缓冲区如mulog_buf,在读函数里面把缓冲区里面的数据给/proc/mymsg,应用程序通过cat /proc/mymsg就会进入到内核态,发现是虚拟文件系统的文件,找到它的file_operation结构体,调用读函数,在读函数里面调用copy_to_user
#define MYLOG_BUF_LEN 1024
//全局指针 struct proc_dir_entry *myentry; //缓冲区大小为1024字节 static char mylog_buf[MYLOG_BUF_LEN]; static char tmp_buf[MYLOG_BUF_LEN]; static int mylog_r = 0; static int mylog_r_for_read = 0; static int mylog_w = 0; //声明1个等待队列头 static DECLARE_WAIT_QUEUE_HEAD(mymsg_waitq);/*环形缓冲区函数:空、写、读、满*/
//空函数
static int is_mylog_empty(void){
//读写位置相等
return (mylog_r == mylog_w); } static int is_mylog_empty_for_read(void) { return (mylog_r_for_read == mylog_w); } //满函数 static int is_mylog_full(void){
//(写位置+1)%长度=读位置
return ((mylog_w + 1)% MYLOG_BUF_LEN == mylog_r); } //写入1个数据 static void mylog_putc(char c){
//如果缓冲区满了
if (is_mylog_full()) { /* 丢弃一个数据 */扔掉的数据是读指针指向的数据,读指针向移移 mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN; if ((mylog_r_for_read + 1) % MYLOG_BUF_LEN == mylog_r)//1:15分钟视频有解析 { mylog_r_for_read = mylog_r;//更新读指针 } } //写数据 mylog_buf[mylog_w] = c; mylog_w = (mylog_w + 1) % MYLOG_BUF_LEN;//写指针向后移1位 /* 唤醒等待数据的进程 */ wake_up_interruptible(&mymsg_waitq); /* 唤醒休眠的进程 */ } //取数据 static int mylog_getc(char *p){
//如果缓冲区为空,返回0
if (is_mylog_empty()) { return 0; } *p = mylog_buf[mylog_r];//非空取出数据 mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN;//读指针向后移 return 1; } //读取字符 static int mylog_getc_for_read(char *p){
//如果缓冲区里面没有数据
if (is_mylog_empty_for_read()) { return 0; }*p = mylog_buf[mylog_r_for_read];
//读位置往后移
mylog_r_for_read = (mylog_r_for_read + 1) % MYLOG_BUF_LEN; return 1; } //把打印信息输出到mylog_buf里面 int myprintk(const char *fmt, ...) { va_list args; int i; int j;va_start(args, fmt);
//把fmt里面的信息输出到tmp_buf里面,i是tmp_buf中数据量的大小
i = vsnprintf(tmp_buf, INT_MAX, fmt, args); va_end(args); //把临时缓冲区的数据放到mylog_buf环形缓冲区 for (j = 0; j < i; j++) mylog_putc(tmp_buf[j]); return i; }//读函数
如果应用程序以非阻塞方式打开我们在proc下创建的文件mymsg,
static ssize_t mymsg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { int error = 0; int i = 0; char c;/* 把mylog_buf的数据copy_to_user, return */
//文件以非阻塞方式打开,并且环形缓冲区是空的就立刻返回
if ((file->f_flags & O_NONBLOCK) && is_mylog_empty_for_read()) return -EAGAIN; 加打印语句看卡在哪里 //printk("%s %d\n", __FUNCTION__, __LINE__);//打印哪一个函数,哪一行 //printk("count = %d\n", count);//打印需要读的数据数 //printk("mylog_r = %d\n", mylog_r);//读位置 //printk("mylog_w = %d\n", mylog_w);//写位置 //等待环形缓冲区非空 ,mymsg_waitq参数是队列,休眠时,(写数据时)以后吧你唤醒,到队列里找到进程唤醒,所以进程先放到1个队列。 error = wait_event_interruptible(mymsg_waitq, !is_mylog_empty_for_read()); 条件不成立就会休眠
//环形后再次打印
//printk("%s %d\n", __FUNCTION__, __LINE__); //printk("count = %d\n", count); //printk("mylog_r = %d\n", mylog_r); //printk("mylog_w = %d\n", mylog_w);/* copy_to_user */吧数据拷贝到用户空间
//如果没有错误且获得字符串,i表示读到的数据数,还没有达到count值,先获得数据存在c中
mylog_getc_for_read
while (!error && (mylog_getc_for_read(&c)) && i < count) { error = __put_user(c, buf);//把数据拷贝到用户空间 buf++;//用户空间的buf++ i++; } if (!error)//如果一直没有错误 error = i;//返回读到的数据个数 return error; } static int mymsg_open(struct inode *inode, struct file *file){
//保存读变量的初始值,为了能重复打印信息
mylog_r_for_read = mylog_r; return 0; } //file_operation结构体(里面有打开函数和读函数) const struct file_operations proc_mymsg_operations = { .open = mymsg_open, .read = mymsg_read, };//入口函数
创建了proc下面的1个条目(文件mymsg),以后读写这个文件的时候就会调用到proc_mymsg_operations这个结构体,调用里面的读函数
static int mymsg_init(void) { //第1个参数文件名字是mymsg,第2个参数是属性(只读),第3个参数是parent,表示proc这个根目录里面 myentry = create_proc_entry("mymsg", S_IRUSR, &proc_root); if (myentry) myentry->proc_fops = &proc_mymsg_operations; return 0; } //出口函数 static void mymsg_exit(void) { remove_proc_entry("mymsg", &proc_root);//删除创建的文件 } module_init(mymsg_init); module_exit(mymsg_exit); //myprintk函数要导出来给其他驱动调用 EXPORT_SYMBOL(myprintk); //添加GPL 协议MODULE_LICENSE("GPL");
#################################################
#################################################
另外1个驱动程序调用myprintk函数打印信息(用first_drv.c)
调用时声明是1个外部函数
在open函数里面调用myprintk函数打印信息