前面讲了proc文件系统的编程,其实在使用API的时候注意一下proc文件系统的实现也是很有益处的。在学习使用proc的时候我顺便看了一下proc的底层实现,发现原理非常简单,毕竟是一个基于内存的文件系统。
首先众所周知,所有的文件系统都位于VFS的层,proc也不例外。在内核启动的时,start_kernel在完成了VFS的初始化不久就调用了:
- #ifdef CONFIG_PROC_FS
- proc_root_init();
- #endif
用于初始化proc文件系统:
fs/proc/root.c- void __init proc_root_init(void)
- {
- struct vfsmount *mnt;
- int err;
- proc_init_inodecache();
- err = register_filesystem(&proc_fs_type); //向VFS注册procfs
- if (err)
- return;
- mnt = kern_mount_data(&proc_fs_type, &init_pid_ns);
- if (IS_ERR(mnt)) {
- unregister_filesystem(&proc_fs_type);
- return;
- }
- init_pid_ns.proc_mnt = mnt;
- proc_symlink("mounts", NULL, "self/mounts"); //创建“mounts”符号连接文件
- proc_net_init(); //创建“net”符号连接及内部目录树结构
- #ifdef CONFIG_SYSVIPC
- proc_mkdir("sysvipc", NULL); //创建“sysvipc”目录
- #endif
- proc_mkdir("fs", NULL); //创建“fs”目录
- proc_mkdir("driver", NULL); //创建“driver”目录
- proc_mkdir("fs/nfsd", NULL); /* somewhere for the nfsd filesystem to be mounted */
- #if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE)
- /* just give it a mountpoint */
- proc_mkdir("openprom", NULL);
- #endif
- proc_tty_init(); //创建“tty”目录及内部结构
- #ifdef CONFIG_PROC_DEVICETREE
- proc_device_tree_init();
- #endif
- proc_mkdir("bus", NULL); //创建“bus”目录
- proc_sys_init(); //创建“sys”目录并初始化
- }
在这之后,许多内核组件和模块就可以向proc文件系统及其子目录添加目录和文件了。其中最重要的数据结构体就是:
struct proc_dir_entry,她在
《内核proc文件系统与seq接口(2)---内核proc文件系统编程接口》介绍过。
她相当于proc文件系统中的数据节点,procfs中许多文件、符号连接和目录文件都是由她表示的。但是请注意,并不是左右的proc文件都对应这样的数据结构,对于/proc/sys、/proc/${PID}/等特定目录下的文件时需要时动态生成的,有其自己的内部实现。而这个proc_dir_entry只对普通的proc文件实现,及我们自己创建的proc文件。
其根目录的表示:
fs/proc/root.c- /*
- * This is the root "inode" in the /proc tree..
- */
- struct proc_dir_entry proc_root = {
- .low_ino = PROC_ROOT_INO,
- .namelen = 5,
- .name = "/proc",
- .mode = S_IFDIR | S_IRUGO | S_IXUGO,
- .nlink = 2,
- .count = ATOMIC_INIT(1),
- .proc_iops = &proc_root_inode_operations,
- .proc_fops = &proc_root_operations,
- .parent = &proc_root,
- };
除了这个节点的结构体在代码中静态定义的之外,其他所有的节点都是系统和模块初始化产生的。也除了这个节点的mane字符串是在内核映像的只读数据段中,其他的节点的name字符串都是在创建的时候紧跟在其struct proc_dir_entry之后的,如下图:
而看了源码(fs/proc/generic.c)你就会很快的了解这个文件系统的数据连接方式:用*next指针通过单向链表连接同目录下文件;用*parent指针指向父目录的proc_dir_entry结构体;用*subdir指针指向本目录中的文件(如果这个文件目录的话),总体底层结构示意图如下所示:
上图只是一个结构示意图,一些指针的指向不确定的,比如proc_root的*subdir肯定不是指向sys,因为初始化之后还有很多目录和文件要产生。但是/root目录下单向链表的最后一个应该是mounts,因为他是最早注册的(起码在3.0上是这样)。上图同时示意了符号连接注册后的一般情况。
对于一个目录注册后的一般如下,主要是注意*proc_iops inode操作函数和*proc_fops 文件操作函数的注册:
(1)使用proc的默认操作函数集proc_file_operations,但必须实现回调函数*read_proc和*write_proc,示意图如下:
(2)使用自己实现的file_operations,以下用/proc/config.gz文件的实现做示意:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
以上的解析只是对procfs的浅析,有兴趣的朋友可以自己看看fs/proc/generic.c,非常简单的文件系统实现,是一个很好的学习样本。RTFSC – Read The Fucking Source Code。
阅读(2222) | 评论(0) | 转发(0) |