Chinaunix首页 | 论坛 | 博客
  • 博客访问: 157076
  • 博文数量: 152
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 0
  • 用 户 组: 普通用户
  • 注册时间: 2016-07-08 15:46
文章分类

全部博文(152)

文章存档

2016年(152)

我的朋友

分类: LINUX

2016-06-02 14:00:04

    前面讲了proc文件系统的编程,其实在使用API的时候注意一下proc文件系统的实现也是很有益处的。在学习使用proc的时候我顺便看了一下proc的底层实现,发现原理非常简单,毕竟是一个基于内存的文件系统。
    首先众所周知,所有的文件系统都位于VFS的层,proc也不例外。在内核启动的时,start_kernel在完成了VFS的初始化不久就调用了:

  1. #ifdef CONFIG_PROC_FS
  2.     proc_root_init();
  3. #endif
用于初始化proc文件系统:

fs/proc/root.c
  1. void __init proc_root_init(void)
  2. {
  3.     struct vfsmount *mnt;
  4.     int err;

  5.     proc_init_inodecache();
  6.     err = register_filesystem(&proc_fs_type); //向VFS注册procfs
  7.     if (err)
  8.         return;
  9.     mnt = kern_mount_data(&proc_fs_type, &init_pid_ns);
  10.     if (IS_ERR(mnt)) {
  11.         unregister_filesystem(&proc_fs_type);
  12.         return;
  13.     }

  14.     init_pid_ns.proc_mnt = mnt;
  15.     proc_symlink("mounts", NULL, "self/mounts"); //创建“mounts”符号连接文件

  16.     proc_net_init(); //创建“net”符号连接及内部目录树结构

  17. #ifdef CONFIG_SYSVIPC
  18.     proc_mkdir("sysvipc", NULL); //创建“sysvipc”目录
  19. #endif
  20.     proc_mkdir("fs", NULL); //创建“fs”目录
  21.     proc_mkdir("driver", NULL); //创建“driver”目录
  22.     proc_mkdir("fs/nfsd", NULL); /* somewhere for the nfsd filesystem to be mounted */
  23. #if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE)
  24.     /* just give it a mountpoint */
  25.     proc_mkdir("openprom", NULL);
  26. #endif
  27.     proc_tty_init(); //创建“tty”目录及内部结构
  28. #ifdef CONFIG_PROC_DEVICETREE
  29.     proc_device_tree_init();
  30. #endif
  31.     proc_mkdir("bus", NULL); //创建“bus”目录
  32.     proc_sys_init(); //创建“sys”目录并初始化
  33. }
    在这之后,许多内核组件和模块就可以向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
  1. /*
  2.  * This is the root "inode" in the /proc tree..
  3.  */
  4. struct proc_dir_entry proc_root = {
  5.     .low_ino    = PROC_ROOT_INO,
  6.     .namelen    = 5,
  7.     .name        = "/proc",
  8.     .mode        = S_IFDIR | S_IRUGO | S_IXUGO,
  9.     .nlink        = 2,
  10.     .count        = ATOMIC_INIT(1),
  11.     .proc_iops    = &proc_root_inode_operations,
  12.     .proc_fops    = &proc_root_operations,
  13.     .parent        = &proc_root,
  14. };
    除了这个节点的结构体在代码中静态定义的之外,其他所有的节点都是系统和模块初始化产生的。也除了这个节点的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 文件操作函数的注册:

    
    在《内核proc文件系统与seq接口(2)---内核proc文件系统编程接口》中曾经介绍过,对于创建 /proc 入口,有两种选择:
(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。
阅读(514) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~