linux proc文件系统-属性文件使用之读写
2016-08-20 16:14
507 查看
1.记录目的
之前在看其他驱动的时候,经常见到创建proc文件系统文件的代码。虽然经常见到,但是我对proc还不会用,所以今天就特意写了个测试程序,来记录下proc代码如何使用。当然调试例程的时候,也遇到了一些错误,后面会贴出来,和大家分享。测试代码在PC机上实现,并验证过。如果博文中有不对的地方,欢迎网友指出,我们共同进步。2.认识一些重要的结构和函数
proc文件系统属性文件和驱动中创建的设备属性文件很相识,它们都可以达到同样的目的。我们这里只讨论使用,如果想分析原理,还是建议read the fucking code。在开始介绍代码之前,大家有必要认识一下seq_operations这个结构,如下所示。struct seq_operations { void * (*start) (struct seq_file *m, loff_t *pos); void (*stop) (struct seq_file *m, void *v); void * (*next) (struct seq_file *m, void *v, loff_t *pos); int (*show) (struct seq_file *m, void *v); };start:它的作用是在设置访问的起始点。设置好了访问起始点,seq_file内部机制可能会使用show方法获取start返回值指向的结构体中的数据到内部缓存,并适时送往用户空间。
m:指向的是本seq_file的结构体,在正常情况下无需处理。
pos:是一个整型位置值,指示开始读取的位置。对于这个位置的意义完全取决于底层实现,不一定是字节为单位的位置,可能是一个元素的序列号。
返回值如果非NULL,则是一个指向迭代器实现的私有数据结构体指针。如果访问出错则返回NULL
stop:在stop实现中,参数m指向本seq_file的结构体,在正常情况下无需处理。而v是指向上一个next或start返回的元素指针。在需要做退出处理的时候才需要实现具体的功能。但是许多情况下可以直接返回。
next:在next实现中应当递增pos指向的值,但是具体递增的数量和迭代器的实现有关,不一定是1。而next的返回值如果非NULL,则是下一个需要输出到缓存的元素指针,否则表明已经输出结束,将会调用stop方法做清理。
v:是之前调用start或next返回的元素指针,可能是上一个show已经完成输出所指向的元素。
pos:需要移动到的元素索引值。
show:负责将v指向元素中的数据输出到seq_file的内部缓存,具体请看代码中所示。其中的参数v就是next返回值。
3.read过程及层次结构
read//代码段位于kernel/seq_file.c ------seq_read() pos = m->index; p = m->op->start(m, &pos); //这里调了一次start while (1) { err = PTR_ERR(p); if (!p || IS_ERR(p)) break; err = m->op->show(m, p); //show time if (err < 0) break; if (unlikely(err)) m->count = 0; if (unlikely(!m->count)) {[ 7122.899602] pos_start=0 p = m->op->next(m, p, &pos); //next指针下移动 m->index = pos; continue; } if (m->count < m->size) goto Fill; m->op->stop(m, p); kvfree(m->buf); m->buf = seq_buf_alloc(m->size <<= 1); if (!m->buf) goto Enomem; m->count = 0; m->version = 0; pos = m->index; p = m->op->start(m, &pos); //又调了一次start。 } m->op->stop(m, p); m->count = 0; goto Done;经过上面的折腾,我们打印顺序确实如上面一样(log代码看下方代码)
[ 7122.899602] pos_start=0
[ 7122.899613] show time!
[ 7122.899618] pos_next=0
[ 7122.899621] show time!
[ 7122.899624] pos_next=1
[ 7122.899712] pos_start=2
层次结构:
下面这个层次图引用自博文,大家可以看看。
4.代码分析
/************************************************************************* > File Name: proc.c > Author: armwind > Mail: armwind@163.com > Created Time: Tue 16 Aug 2016 19:30:38 CST ************************************************************************/ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/init.h> #include <linux/device.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <asm/uaccess.h> static DEFINE_MUTEX(registration_lock); static struct list_head head; struct test_data { struct list_head list; char *value; }; static void *test_seq_start(struct seq_file *m, loff_t *pos) { mutex_lock(®istration_lock); printk(KERN_ALERT "pos_start=%d\n",*pos); //打印标记 return seq_list_start(&head,*pos); } static void *test_seq_next(struct seq_file *m, void *v, loff_t *pos) { printk(KERN_ALERT "pos_next=%d\n",*pos); return seq_list_next(v,&head, pos); //返回的地址就是给show函数第二个参数使用 } static void test_seq_stop(struct seq_file *m, void *v) { mutex_unlock(®istration_lock); //这里一般什么都不做。 } static int test_seq_show(struct seq_file *m, void *v) { struct test_data *data = list_entry(v,struct test_data,list); //v就是next函数返回过来的,它就是当前要查看的节点。 printk(KERN_ALERT "show time!\n"); seq_printf(m,"value: %s\n", data->value); return 0; } static ssize_t add_data(const char __user* buffer, size_t count) { struct test_data *data; if (NULL == buffer){ //注意指针在使用之前要判断一下,保护。 printk(KERN_ALERT "kzalloc mem failed\n"); return -1; } mutex_lock(®istration_lock); char *tmp = kzalloc((count+1), GFP_KERNEL); if (!tmp){ printk(KERN_ALERT "kzalloc mem failed\n"); return -ENOMEM; } if (copy_from_user(tmp, buffer, count)){ kfree(tmp); return EFAULT; } data = kmalloc(sizeof(*data), GFP_KERNEL); if (data == NULL) printk(KERN_ALERT "kmalloc mem failed\n"); list_add(&data->list,&head); data->value = tmp; mutex_unlock(®istration_lock); } static ssize_t my_write(struct file*file,const char __user* buffer, size_t count, loff_t *ppos) { add_data(buffer,count); return count; } static const struct seq_operations proc_test_seq_ops = {//标准的seq_operation操作集合 .start = test_seq_start, .next = test_seq_next, .stop = test_seq_stop, .show = test_seq_show, }; static int proc_test_open(struct inode *inode, struct file *file) { return seq_open(file, &proc_test_seq_ops); //prco属性文件和proc_test_seq_ops绑定 } static const struct file_operations test_proc_fops = { .owner = THIS_MODULE, .open = proc_test_open, .read = seq_read, .write = my_write, .llseek = seq_lseek, .release = seq_release, }; struct proc_dir_entry *entry = NULL; int hello_init(void) { struct proc_dir_entry *son = NULL; struct proc_dir_entry *root= NULL; mutex_init(®istration_lock); //这里变量在初始化时,一定要初始化。 INIT_LIST_HEAD(&head); //记得初始化。 root = proc_mkdir("father",NULL); //在proc目录下,建立一个father目录。 entry = proc_create("test",0666, root, &test_proc_fops); //test属性文件和test_proc_fops操作集合绑定。 if (!entry){ printk(KERN_ALERT "create proc dir failed\n"); return -1; } son = proc_mkdir("son",root);//这里仅仅是为了测试,没具体意义。 printk(KERN_ALERT "hello init ok\n"); return 0; } void hello_exit(void) { proc_remove(entry); printk(KERN_ALERT "Hello Exit!\n"); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL");效果:上面的模块编译运行后就会在proc目录创建一个father和son目录,加上一个test属性文件。
测试结果:
第一去cat test文件时,可以发现什么都没有。后面相继写入,打印,结果大家看看吧。
相关文章推荐
- Linux3.10内核之后proc文件系统的使用
- Linux3.10内核之后proc文件系统的使用
- 使用 /sys 文件系统访问 Linux 内核:比/proc 更为理想的访问内核数据的途径
- 使用 /sys 文件系统访问 Linux 内核:比/proc 更为理想的访问内核数据的途径
- linux 使用/proc文件系统 实现用户空间与内核模块之间通信
- linux文件系统的系统分析--(九)sysfs下属性文件的读写
- Linux mount 修改文件系统的读写属性
- 使用 /sys 文件系统访问 Linux 内核:比/proc 更为理想的访问内核数据的途径
- Linux /proc 虚拟文件系统使用
- linux环境下使用XFS文件系统
- 2.6 使用Linux文件系统
- [IBM Developer]使用 /proc 文件系统来访问 Linux 内核的内容
- 文件系统管理 之 Linux 查看磁盘分区、文件系统、使用情况的命令和相关工具介绍
- 转 Linux 查看磁盘分区、文件系统、使用情况的命令和相关工具介绍
- Windows 到 Linux 之旅: 第 6 部分. 使用分区和文件系统
- Linux系统特殊文件--目录概念及使用
- 在 Linux 中使用 ReiserFS 文件系统
- 无法使用系统显示隐藏文件属性
- 使用 /proc 文件系统来访问 Linux 内核的内容
- 实时管理Linux内核——使用 /proc 文件系统来控制系统