本文共 5541 字,大约阅读时间需要 18 分钟。
首先我们需要弄清楚proc机制,来看看fs/proc/proc_misc.c这个文件,从入口函数开始看:
proc_misc_init(void) #ifdef CONFIG_PRINTK{ struct proc_dir_entry *entry; entry = create_proc_entry("kmsg", S_IRUSR, &proc_root);//这里创建了一个proc入口kmsg if (entry) entry->proc_fops = &proc_kmsg_operations;//设置操作函数,见注释1}
const struct file_operations proc_kmsg_operations = { .read= kmsg_read, .poll= kmsg_poll, .open= kmsg_open, .release= kmsg_release,};
#include#include #include #include #include #include #include #include //#include //#include #include struct proc_dir_entry *myentry;const struct file_operations proc_mymsg_operations = {};static int mymsg_init(void){ myentry = create_proc_entry("mymsg", S_IRUSR, &proc_root); if (myentry) myentry->proc_fops = &proc_mymsg_operations ; return 0;}void mymsg_eixt(void){ remove_proc_entry("mymsg", &proc_root);} module_init(mymsg_init);module_exit(mymsg_eixt);
#include/*因为 myprintk是我们自己写的打印语句#include #include #include #include #include #include #include //#include //#include #include #define MYLOG_BUF_LEN 1024struct proc_dir_entry *myentry;static char mylog_buf[MYLOG_BUF_LEN];static char tmp_buf[MYLOG_BUF_LEN];static int mylog_r = 0; //用来标识读static int mylog_w = 0; //用来标识写static DECLARE_WAIT_QUEUE_HEAD(mymsg_waitq);//判断环形缓冲区是否为空static int is_mylog_empty(void){ return (mylog_r == mylog_w);}//判断环形缓冲区是否已满static int is_mylog_full(void){ return ((mylog_w + 1)% MYLOG_BUF_LEN == mylog_r);}/*写缓冲区:如果缓冲区已满的话,就让覆盖掉下一个要读的数据* 否则就直接写入此外在写缓冲区函数里面还需要做的一件事情就是唤醒等待队列,这是因为当缓冲区为空的时候,如果调用读函数的话,就会使进程进入等待队列,理当在写入数据的时候唤醒进程*/static void mylog_putc(char c){ if (is_mylog_full()) { /* 丢弃一个数据 */ mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN; } mylog_buf[mylog_w] = c; mylog_w = (mylog_w + 1) % MYLOG_BUF_LEN; /* 唤醒等待数据的进程 */ wake_up_interruptible(&mymsg_waitq); /* 唤醒休眠的进程 */}/*读缓冲区:如果缓冲区为空的话,就返回0否则从首部读出一个数据,返回1*/static int mylog_getc(char *p){ if (is_mylog_empty()) { return 0; } *p = mylog_buf[mylog_r]; mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN; return 1;}/*打印函数:这个函数是参考sprintf函数得编写的*它将传递进来的参数转换为固定的格式之后,放入到一个临时缓冲区里面*然后将环形缓冲区的值写入到mylog_buf缓冲区里面,详见注释2*/int myprintk(const char *fmt, ...){ va_list args; int i; int j; va_start(args, fmt); i = vsnprintf(tmp_buf, INT_MAX, fmt, args); //将传进来的参数转换后放入tmp_buf va_end(args); for (j = 0; j < i; j++) mylog_putc(tmp_buf[j]); //将tmp_buf里面的东东放入mylog_buf缓冲区里面 return i;}/*读函数:当在应用空间调用命令:cat /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 */ //如果为非阻塞且mylog_buf为空,那么就出错返回 if ((file->f_flags & O_NONBLOCK) && is_mylog_empty()) return -EAGAIN; //如果mylog_buf为空的话进程进入等待队列,还记得我们在写缓冲区 //函数里面会唤醒进程这件事情吧! error = wait_event_interruptible(mymsg_waitq, !is_mylog_empty()); /* copy_to_user */ //首先从缓冲区里面获得一个字符,然后拷贝到用户空间 //如果缓冲区还有信息的话,就再次获得字符,拷贝到用户 //空间,直到缓冲区为空 while (!error && (mylog_getc(&c)) && i < count) { error = __put_user(c, buf); //将c的内容拷贝到用户空间 buf++; i++; } if (!error) error = i; return error;}const struct file_operations proc_mymsg_operations = { .read = mymsg_read,};static int mymsg_init(void){ 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);EXPORT_SYMBOL(myprintk);MODULE_LICENSE("GPL");
#include上两个C文件要放到同一个目录下,Makefile如下:#include #include #include #include #include #include #include //#include //#include extern int myprintk(const char *fmt, ...);static int first_drv_init(void){ myprintk("first_drv_init\n"); return 0;}static void first_drv_exit(void){ myprintk("abcdefhg\n");}module_init(first_drv_init);module_exit(first_drv_exit);MODULE_LICENSE("GPL");
obj-m := kproctest3.o kproctest.oKERNELDIR := /lib/modules/2.6.23-gentoo-r9/build/PWD :=$(shell pwd)modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules.PHONY :cleanclean: rm -rf *.o *ko
转载地址:http://hjzub.baihongyu.com/