当前位置:Linux教程 - Linux - 特殊文件系统proc

特殊文件系统proc

特殊文件系统/proc:

作者 :noble;
写作时间:2002-9-4;
测试环境:Red Hat Linux 7.2
联系方法:QQ:4440151
Email:[email protected]

1. 对kern_mout()进行解析:
(1) 调用函数get_unnamed_dev()为文件系统/proc文件系统分配一个设备号。
(2) 调用函数read_super对应的函数proc_read_super()分配super_block,inode,dentry;(a)其中super_block是在函数read_super中新生成的;
(b)inode是调用函数proc_get_inde()新生成的;
(c)dentry由函数d_alloc_root()创建的;
注:根目录的inode和dentry创建的依据是一个proc_dir_entry结构proc_root,这个结构相当于一般文件系统中硬盘上的(不是内存中的inode或dentry结构)目录项之类的东西。
(3) 调用add_vfsmnt()创建vfsmount结构,同时对其进行设置(如和super_block的关系等)。并使得proc文件系统file_system_type数据结构的kern_mnt指向这个vfsmount结构。但是这并不意味着path_walk()就能顺着路径名“/proc”找到proc文件系统的根节点,因为path_walk()并不涉及file_system_type数据结构,而/proc的inode结构和dentry结构并没有和新创建的这个vfsmount结构联系起来。
2. 所以,光调用kern_mount()还不够,还得由系统调用的初始化进程从内核外部调用系统调用mount(),再安装一次,通常的命令行为:mount –nvt proc/dev/null /proc,也就是说,把建立再“空设备”/dev/null上的proc文件系统安装再节点/proc 上。
在mount()中,因为file_system_type结构中FS-SINGLE标志位为1,所以要调用get_sb_single()来得到相应的超级块,其方法和别的不同(别的都是从设备上读),代码中通过file_system_type结构中的指针kern_mnt取得文件系统vfsmount结构,从而取得其super_block结构,而这个关系正是在kern_mount()中设置好的。
Mount()中其他的操作就和普通的文件系统的安装无异了。这样就把proc文件系统安装到了节点/proc上。
3. 因为整个proc文件系统都不存在于设备上,所以不光是它的根节点需要在内存中创造出来,自根节点以下的所以节点全都需要在运行时加以创建,这是由内核在初始化时调用proc_root_init()完成的。
(1) 首先是直接在/proc下面的叶子节点,即文件节点,这是由proc_misc_init()创建的。
函数proc_misc_init()调用函数create_proc_read_entry()进行分配结构,而create_proc_read_entry()则调用函数create_proc_entry(),下面对函数create_proc_entry()进行解析:
a) 调用函数xlate_proc_name()返回父亲节点的proc_dir_entry结构;
b) 调用函数kmalloc()分配自己的proc_dir_entry结构;
c) 对自己的proc_dir_entry结构进行初始化;
d) 调用函数proc_register()对自己的proc_dir_entry结构进行注册登记,即挂到父亲节点的proc_dir_entry结构内的subdir队列中;
(2) 创建三个特殊的文件节点,这三个文件是:kmsg、kcore、profile;这些文件的创建直接调用create_proc_entry();
(3) 创建直接在/proc目录中的子目录,如:net,fs,dirver等;这些子目录都是通过proc_mkdir()创建的。
(4) 除了这些子目录之外,就只有/proc/tty是特殊的,因为其他的只有两层目录/proc/dirname,只有/proc/tty是一颗子树,即下面还有目录,所以要专门创建,调用的函数是:proc_tty_init();
(5) 此外,如果系统不采用传统的基于主设备号/次设备号的/dev设备(文件)目录,而采用树状的设备目录/device_tree,则还要在proc_root_init()中创建/device_tree子树。这是由proc_device_init()完成的。

4./porc文件系统的目录搜寻
注:/proc文件系统中,除了根节点外,其他节点只有proc_dir_entry 结构,没有dentry结构和inode结构。
1)搜寻/proc/loadavg:
(1) 调用path_walk(),到根节点后,要调用函数cached_lookup(),先在内存搜索下一个节点的dentry结构,如果没在内存中找到,就从设备上读取,对应的函数是real_lookup();
(2) 从设备上的读操作具体依赖于其父亲节点的inode结构中的指针I_op指向那一个inode_operations数据结构。对于节点/proc,它的I_op指针指向proc_root_inode_operations,这是在它的proc_dir_entry结构proc_root中静态定义好的。
(3) /proc文件系统对应的读inode节点的函数是proc_lookup(),这个函数根据要查找节点的proc_dir_dentry结构,填充一个dentry结构,创建一个inode结构,然后返回。
(4) 以后的操作就和一般节点的path_walk()没什么区别了。
(5) 下面对其读操作进行解析:
(a) 为读文件提供的操作函数是proc_file_read(),这是一个为proc特殊文件通用的函数。
(b) 从代码中可以看出,具体的读操作是通过该节点的proc_dir_entry结构中的函数指针get_info或read_proc提供的。其中get_info是为了与老一版本兼容而保留的,现在都已改用read_proc。
(c) 对于/proc/loadavg来说,读文件指针指向loadavg_read_proc();它的作用就是将数组avenrum[]中积累的在过去1分钟、5分钟以及15分钟内的系统平均CPU负荷等统计信息通过sprintf()“打印”到缓冲区页面中。
(d) 在函数proc_file_read()中,调用该节点读函数读完以后,将把读的信息通过函数copy_to_user(),拷贝的用户空间。
2)搜寻/proc/self/cwd;
(1) 同上,调用proc_root_lookup()->proc_pid_lookup();
注:
a)对于有proc_dir_entry结构的节点,即向proc_root登记而挂入了其队列中的节点,也就是上一种情景,其查找目标节点所调用的函数依次是:
path_walk()->real_lookup()->proc_root_lookup()->proc_lookup();
注意:在real_lookup()中分配dentry结构,proc_root_lookup()是根据inode节点中提供的操作,在proc_lookup()中分配inode结构。

b)另一种情况是对应于当前系统中的各个进程而并不存在proc_dir_entry结构的节点。其查找目标节点所调用的函数依次是:
path_walk()->real_lookup()->proc_root_lookup()->proc_pid_lookup();
注意:proc_lookup()和proc_pid_lookup()都在函数proc_root_lookup()中。
(2)从proc_root_lookup()返回到path_walk()中以后,接着要检查和处理两件事,第一件事新找到的节点是否为安装点;第二件就是它是否是一个链接点。这正是我们所关心的,因为/proc/self就是个链接点。
(3)对于链接点,通过do_follow_link(),调用该inode结构中的inode_operations结构提供的follow_link函数指针,寻找目标节点。
Follow_link指向函数proc_self_follow_link(),这个函数又调用vfs_follow_lnk();
(4)要指出的是,在vfs_follow_link()中,将会递归的调用path_walk()来寻找目标节点,所以又会调用其父亲节点/proc的lookup函数,即proc_root_lookup(),不同的是这次寻找的不是”self”,而是当前进程的pid字符串。这一次,在proc_root_lookup()中对proc_lookup()的调用同样会因为在proc_root的subdir队列中找不到相应的proc_dir_entry结构而失败,所以也要进一步调用proc_pid_lookup()寻找。可是这一次的节点名不似乎“self”了,所以走的路线页就不同了。
(5)这次调用proc_pid_lookup()所起的作用是将要找的函数名(当前进程号的字符串形式)转换成一个无符号整数,然后依此为pid从系统中寻找是否存在相应的进程。如果找到了相应的进程,就通过proc_pid_make_inode()为之创建一个inode结构,并初始化已经分配的dentry结构。
(6)这样,从path_walk()->do_follow_link()返回以后,就继续搜寻下一个节点“cwd”,这一次搜索的是最后一个节点,所以跳到:las_component处。
(7)但是,同样也要调用函数real_lookup(),从设备上读取inode结构(其实是重新创建一个inode结构)。在real_lookup()函数中要调用的函数是其父亲节点的inode_operation结构中的lookup函数指针指向的函数,这个函数是proc_base_inode_operation()。
(8)在函数proc_base_inode_operation()中,创建inode结构,并根据不同的类型进行初始化。注意,inode 结构中的union被用作一个proc_inode_info结构proc_I,这个结构中的tast指针在函数proc_pid_make_inode()中被设定为该进程的task_struct指针。
另外,还要设置的两项是:
(a) 将inode结构中的指针I_op设置成指向proc_pid_link_inode_operation数据结构。
(b) 将inode结构中替换union的proc_inode_info结构中的函数指针proc_get_link设置为指向函数proc_cwd_link();
(9)这样,从proc_base_inode_operation()返回到(7):real_lookup(),进而返回到path_walk()时,nameidata结构中的指针dentry已经指向这个特定“cwd”节点的dentry结构,但是接着同样要受到对其inode结构中的I_op指针以及相应inode_operation结构中的指针follow_link的检查。因为follow_link非0,在(8)被设置。这样,就可以找到当前工作目录,其方法见(10);
(10)如前所述,节点的inode中的union用作一个proc_inode_info结构,其中的指针task指向相应进程的task_struct结构,进而可以得到这个进程的fs_struct结构,而这个数据结构中的指针pwd即指向该进程的”当前工作目录“的dentry结构,同时指针pwdmnt指向该目录所在设备安装时的vfsmount结构。