分类: LINUX
2009-06-08 10:58:21
1、重要的数据结构: struct proc_dir_entry { unsigned int low_ino; unsigned short namelen; const char *name; mode_t mode; nlink_t nlink; uid_t uid; gid_t gid; loff_t size; const struct inode_operations *proc_iops; /* * NULL ->proc_fops means "PDE is going away RSN" or * "PDE is just created". In either case, e.g. ->read_proc won't be * called because it's too late or too early, respectively. * * If you're allocating ->proc_fops dynamically, save a pointer * somewhere. */ const struct file_operations *proc_fops; get_info_t *get_info; struct module *owner; struct proc_dir_entry *next, *parent, *subdir; void *data; read_proc_t *read_proc; write_proc_t *write_proc; atomic_t count; /* use count */ int pde_users; /* number of callers into module in progress */ spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */ struct completion *pde_unload_completion; shadow_proc_t *shadow_proc; }; 2、创建函数: struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent); name: 要创建的文件名称; mode: 该文件的保护掩码; parent: 确定文件所在目录,如果置NULL,则位置为/proc下。 3、读proc: int read_proc(char *page, char **start, off_t off, int count, int *eof, void *data); page:指示用来写入数据的缓冲区; off和count:与read函数对应的参数相同; start和eof:用于读取大于1个page数据时实现。 4、写proc: int write_proc(struct file *file, const char __user *buffer, unsigned long count, void *data); filp 参数实际上是一个打开文件结构(可以忽略这个参数)。Buffer 参数是用户空间要写入的数据。缓冲区地址实际上是一个用户空间的缓冲区,不能直接读取它。len 参数定义了在 buffer 中有多少数据要被写入。data 参数是一个指向私有数据的指针。 5、实现一个proc文件 (1)调用create_proc_entry创建一个struct proc_dir_entry,作为一个全局量。 (2)对创建的struct proc_dir_entry进行赋值:read_proc,mode,owner,size,write_proc等等。 示例: proc_hello.c #include #include #include #define procfs_name "helloworld" struct proc_dir_entry *Our_Proc_File; int procfile_read(char *buffer, char **buffer_location, off_t offset, int buffer_length, int *eof, void *data) { int ret; printk(KERN_INFO "procfile_read (/proc/%s) called\n", procfs_name); /* * We give all of our information in one go, so if the * user asks us if we have more information the * answer should always be no. * * This is important because the standard read * function from the library would continue to issue * the read system call until the kernel replies * that it has no more information, or until its * buffer is filled. */ if (offset > 0) { printk(KERN_INFO "offset > 0\n"); /* we have finished to read, return 0 */ ret = 0; } else { /* fill the buffer, return the buffer size */ printk(KERN_INFO "offset <= 0\n"); ret = sprintf(buffer, "HelloWorld!\n"); } return ret; } int init_module() { Our_Proc_File = create_proc_entry(procfs_name, 0644, NULL); if (Our_Proc_File == NULL) { remove_proc_entry(procfs_name, &proc_root); printk(KERN_ALERT "Error: Could not initialize /proc/%s\n", procfs_name); return -ENOMEM; } Our_Proc_File->read_proc = procfile_read; Our_Proc_File->owner = THIS_MODULE; Our_Proc_File->mode = S_IFREG | S_IRUGO; Our_Proc_File->uid = 0; Our_Proc_File->gid = 0; Our_Proc_File->size = 37; printk(KERN_INFO "/proc/%s created\n", procfs_name); return 0; /* everything is ok */ } void cleanup_module() { remove_proc_entry(procfs_name, &proc_root); printk(KERN_INFO "/proc/%s removed\n", procfs_name); } Makefile: obj-m := proc_hello.o KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: $(RM) *.o *.mod.c *.ko *.symvers 本示例只实现了一个简单的读proc操作。 编译后,执行: # insmod proc_hello.ko # dmesg /proc/helloworld created # cat /proc/ helloworld HelloWorld! # dmesg procfile_read (/proc/helloworld) called offset <= 0 procfile_read (/proc/helloworld) called offset > 0 procfile_read (/proc/helloworld) called offset > 0 # rmmod proc_hello /proc/helloworld 为什么在调用cat /proc/ helloworld后,会看到procfile_read被执行了三次,且只有第一次读到了数据? 因为通常cat,dd等,都是通过标准库函数来实现的,标准库函数往往会多次调用系统调用来读写数据,所以我们看到调用了3次。具体cat以及dd等调用一次,读取或写数据大小,这个需要具体查证一下。 我写了一个test程序,来验证这个多次读操作不是在系统调用里面实现的: test.c #include #include #include #include #include #include #define PROC_FILE "/proc/helloworld" int main() { char buf[50]; int fd = open(PROC_FILE, O_RDONLY); if (fd == -1) { printf("open err\n"); return -1; } if (read(fd, buf, sizeof("HelloWorld")) > 0) { printf("read: %s\n", buf); } close(fd); } 编译: # gcc test.c –o test 加载模块后,执行 # ./test 然后,执行: # dmesg procfile_read (/proc/helloworld) called offset <= 0 确实只执行了一次,这说明多次调用procfile_read是在系统之上做的。 6、注意点 (1)切记不能访问已经卸载的一个proc文件,否则会导致kernel panic; (2)注意不要删除正在调用的问proc文件。因为文件的入口项不存在关联的作者,文件的调用也并没有作用到模块的引用计数上; (3)勿注册两个同名的proc文件,这样将导致入口项无法区分。 以上这些都是proc文件的缺点,也是使用时必须注意的地方。所以,现在推荐使用seq_file 和sys_fs。 |