/proc文件系统是Linux内核的一个虚拟文件系统,说它虚拟,是因为和磁盘分区上的文件系统不同,它只有在内核启动以后,工作起来的时候才会被动态创建。
/proc下面的内容会随着内核的配置和工作的状态在变化,比如/proc/meminfo这个文件,里面的内容是内存信息,不同的机器自然不同;还有就是/proc目录下面用数字命名的子目录,每个子目录的名字也就是数字,对应当前系统正在运行的进程,而子目录里面的内容就是对应进程的信息。
/proc里面还有其他许多信息,这里就不一一描述了,有兴趣可以谷歌一下。
Linux内核经过多年发展,/proc目录下的林林总总,已经是非常多了,虽然已经开发出了新的/sys文件系统替代/proc,但是,许多传统的程序仍然使用/proc文件系统作为内核与用户程序的接口。
如何让内核更高效,那是内核开发人员的事情。作为一个使用内核的开发人员,掌握/proc文件系统的使用方法也是很有用的,和/sys文件系统组织严密的结构相比,用/proc有时候还是很方便的。
不多说废话了,先入个门,介绍/proc的最基本操作。
这一集我们就介绍操作/proc文件系统的三个最基本函数:
代码片段1 定义在内核代码的<linux/proc_fs.h>
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent); struct proc_dir_entry *proc_mkdir(const char *name,struct proc_dir_entry *parent); void remove_proc_entry(const char *name, struct proc_dir_entry *parent);先插一句,本例所有代码使用内核版本2.6.38.2,系统是ubuntu 11.04 amd64,要注意哦。
这三个函数,从函数名称就可以理解出来意思,第一个create_proc_entry()函数用做创建一个节点,也就是创建一个文件;
第二个proc_mkdir()函数建立一个目录;
第三个remove_proc_entry()函数是删除一个节点,实际上,不仅能删除节点,也可以删除proc_mkdir()函数创建的目录。
需要注意的是,proc相关的函数,操作的目标都是在/proc目录下。好,继续看函数参数:
create_proc_entry()函数:name 是节点的名称,一个字符串;mode 访问权限,可以使用八进制表示,chmod命令使用的权限一样;parent 父目录,注意是一个struct proc_dir_entry结构,如果写NULL表示/proc目录。
proc_mkdir()函数:name 子目录名称,一个字符串;parent 父目录,和上面解释一样。
remove_proc_entry()函数:参数同上面一样。
函数介绍完毕。有这三个函数就可以开工了。废话少说,贴代码:
代码片段2 自己写的小模块,演示如何建立删除/proc节点
#include <linux/module.h> #include <linux/init.h> #include <linux/version.h> #include <linux/proc_fs.h> MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("<freshpassport@gmail.com>"); #define USER_ROOT_DIR "pt" #define USER_ENTRY1 "pt_entry1" static struct proc_dir_entry *pt_root; static struct proc_dir_entry *pt_entry1; static int proc_test_init(void) { pt_root = proc_mkdir(USER_ROOT_DIR, NULL); if (NULL==pt_root) { printk(KERN_ALERT "Create dir /proc/%s error!\n", USER_ROOT_DIR); return -1; } printk(KERN_INFO "Create dir /proc/%s\n", USER_ROOT_DIR); pt_entry1 = create_proc_entry(USER_ENTRY1, 0666, pt_root); if (NULL == pt_entry1) { printk(KERN_ALERT "Create entry %s under /proc/%s error!\n", USER_ENTRY1, USER_ROOT_DIR); goto err_out; } printk(KERN_INFO "Create /proc/%s/%s\n", USER_ROOT_DIR, USER_ENTRY1); pt_entry1->read_proc = NULL; pt_entry1->write_proc = NULL; return 0; err_out: remove_proc_entry(USER_ROOT_DIR, pt_root); return -1; } static void proc_test_exit(void) { remove_proc_entry(USER_ENTRY1, pt_root); remove_proc_entry(USER_ROOT_DIR, NULL); printk(KERN_INFO "All Proc Entry Removed!\n"); } module_init(proc_test_init); module_exit(proc_test_exit);一个标准的2.6内核模块写法。proc_test_init()函数是入口,proc_test_exit()函数是出口。
最开始引入了4个头文件,其中3个是内核模块标配的头文件,第4个是主角,proc_fs.h,前面说了,操作/proc的函数就定义在里面。
第9行,USER_ROOT_DIR定义了我们要在/proc目录下建立的子目录名称,也是用户的根目录;第10行,USER_ENTRY1定义了欲在USER_ROOT_DIR下面建立的节点名称。
第13、14行分别定义了USER_ROOT_DIR和USER_ENTRY1对应的目录结构pt_root和pt_entry1。
操作的数据介绍好了,说下流程吧。
proc_test_init()函数:
此函数是加载模块的时候执行的入口函数,会被内核自动调用。第19行,函数一开始就调用proc_mkdir()函数,在/proc目录下建立一个子目录,注意proc_mkdir()函数的第二个参数,NULL表示以/proc作为根目录。
子目录USER_ROOT_DIR建立成功后,紧接着调用create_proc_entry()函数建立一个文件节点,注意函数的第三个参数parent,不是NULL,而是pt_root,表示以USER_ROOT_DIR作为根目录。
文件建立成功后,需要设置pt_entry1的读写回调函数为NULL,这样做的目的是该文件节点读写操作都不处理。
proc_test_exit()函数:
用户调用rmmod卸载内核模块的时候被内核调用。此函数删除已经建立的节点,要注意的是remove_proc_entry()函数的parent实参,这里容易出错,导致内核报错。
最后贴一下Makefile:
obj-m += proc_test1.o all: make -C /usr/src/linux-headers-2.6.38-8-generic M=`pwd` modules clean: make -C /usr/src/linux-headers-2.6.38-8-generic M=`pwd` clean看看执行的效果:
下面是加载模块后列出文件节点
root@iscsia:~/proc_test# ll /proc/pt/ total 0 -rw-rw-rw- 1 root root 0 2011-07-01 15:04 pt_entry1下面是读文件节点/proc/pt/pt_entry1
root@iscsia:~/proc_test# cat /proc/pt/pt_entry1 root@iscsia:~/proc_test#什么结果都没有,正常,因为回调函数都给设置为NULL了。
下面是写文件节点/proc/pt/pt_entry1
root@iscsia:~/proc_test# echo "hello,entry" > /proc/pt/pt_entry1 -bash: echo: write error: Input/output error看来写操作不是很理想,报错了,pt_entry1不是很合作啊,也是,前面都给人家设置成NULL了,也不能全怪人家了。哈哈。下次继续介绍如何进行自己的读写操作。
转载于:https://www.cnblogs.com/Dennis-mi/articles/5239325.html