![](/style/images/good.png)
![](/style/images/bad.png)
“tcp丢包分析”实验解析(一)--proc文件系统
source link: http://mp.weixin.qq.com/s?__biz=MzI3NzA5MzUxNA%3D%3D&%3Bmid=2664607299&%3Bidx=1&%3Bsn=b9dddfef9793edd2d4d644309658c654
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
“tcp丢包分析”系列文章代码来自谢宝友老师,由西邮陈莉君教授研一学生进行解析,本文由戴君毅整理,梁金荣编辑,贺东升校对。
最初开发 /proc
文件系统是为了提供有关系统中进程的信息。但是这个文件系统非常有用, /proc
文件系统包含了一些目录(用作组织信息的方式)和虚拟文件。虚拟文件可以向用户呈现内核中的一些信息,也可以用作一种从用户空间向内核发送信息的手段。
/proc
文件系统可以为用户提供很多信息, 在左边是一系列数字编号,每个实际上都是一个目录,表示系统中的一个进程。由于在Linux中创建的第一个进程是 init
进程,因此它的 process-id
为 1。
右边的目录包含特定信息,比如 cpuinfo
包含了CPU的信息, modules
包含了内核模块的信息。
为了解决一些实际问题,我们需要在 /proc
下创建条目捕获信息,使用文件系统通用方法肯定是不行的,需要使用相关API编写内核模块来实现。
在做谢宝友老师写的“TCP丢包分析”实验里,首先就会在 /proc
下创建条目,较为简单,先来看 init
和 exit
:
框架还是比较清晰的,需要深入源码来感受一下,第一部分代码:
struct proc dir entry *pe;
结构 proc_dir_entry
定义在 <fs/proc/internal.h>
下,可以称为一个 pde
,在创建一个文件或目录时就会创建一个 pde
来管理它们。而在打开它们的时候,则会创建一个 proc_inode
结构:
可以使用 PROC_I
宏,也就是我们熟悉的 container_of
,从虚拟文件系统的 inode
得到 proc_inode
,进而得到 pde
。
回到 proc_dir_entry
结构,很多信息从字段名字就可以看出, pde
需要指向创建自己的父 pde
结构, subdir
的组织方式是红黑树,还需要我们实现操作集以及一些引用计数和命名规则等等。
有意思的是,除了操作集之外还有一个 proc_write_t
,对于一些功能比较简单的 proc
文件,我们只要实现这个函数即可,而不用设置 inode_operations
结构,在注册 proc
文件的时候,会自动为 proc_fops
设置一个缺省的 file_operations
结构。
此时,我们可以想象以下模型:
第二部分代码是:
proc_mkdir("mooc", NULL);
proc_mkdir("mooc/net", NULL);
remove proc entry("mooc/net", NULL);
remove proc entry("mooc", NULL);
易知其功能是在 /proc
下创建和删除条目 mooc/net
,以创建操作为例,看下内核代码如何实现的:
这里逻辑很简单, proc_mkdir
实际上是 proc_mkdir_data
默认了权限为 S_IRUGO|S_IXUGO
,再调用 __proc_create
初始化一个局部 pde
,如果成功则初始化操作集,并调用 proc_register
注册这个 pde
到父 pde
下并返回。
__proc_create
调用 kmem_cache_zalloc
从 cache
中获取空间给 pde
,并且对条目名称进行检查。如果成功,则对名称、模式等属性赋值,设置引用计数并初始化锁。
__proc_register
接收两个参数,一个父亲 pde
,一个当前 pde
,目的是把当前 pde
挂到父亲名下,前面提到 subdir
的组织形式是红黑树,那么肯定涉及相关代码,来看:
首先判断当前 pde
的 id
是否越界,如果没有打开子目录锁,把当前 pde
的 parent
字段指向父亲 pde
,并尝试在红黑树中插入子目录,成功后重新上锁并返回当前 pde
。
红黑树的插入操作篇幅所限不再叙述。
下面看第三部分代码:
proc_create
内部也是调用 proc_create_data
,但还需自行指定权限以及操作集回调,用于创建一个 proc
文件,在3.10内核中取代 create_proc_entry
这个旧的接口。
回到实验代码,我们为加入的条目编写操作集接口。
一般地,内核通过在 procfs
文件系统下建立文件来向用户空间提供输出信息,用户空间可以通过任何文本阅读应用查看该文件信息,但是 procfs
有一个缺陷,如果输出内容大于1个内存页,需要多次读,因此处理起来很难,另外,如果输出太大,速度比较慢,有时会出现一些意想不到的情况, AlexanderViro
实现了一套新的功能,使得内核输出大文件信息更容易,它们叫做 seq_file
,所以在使用它们的操作集时需要包含 seq_file.h
头文件。
Drop_packet_open
实际上是调用了 single_open
:
为什么这么做?内核文档给出了相关描述:
https://www.kernel.org/doc/Documentation/filesystems/seq_file.txt
你可能发现,内核文档里显示的是 seq_open
,而实验里是 single_open
,它们有什么区别呢?实际上内核文档的最后给出了答案:
谢宝友老师的实验中运用 seq_file
的极简版本(extra-simple version),只需定义一个 show()
函数。完整的情况我们还需要实现 start()
, next()
等迭代器来对 seq_file
进行操作。极简版本中, open
方法需要调用 single_open
,对应的, release
方法调用 single_release
。
推荐阅读https://www.ibm.com/developerworks/cn/linux/l-proc.html
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK