23

“tcp丢包分析”实验解析(一)--proc文件系统

 4 years ago
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。

umYJ7bZ.jpg!web

右边的目录包含特定信息,比如 cpuinfo 包含了CPU的信息, modules 包含了内核模块的信息。

3yUBFrB.jpg!web

为了解决一些实际问题,我们需要在 /proc 下创建条目捕获信息,使用文件系统通用方法肯定是不行的,需要使用相关API编写内核模块来实现。

在做谢宝友老师写的“TCP丢包分析”实验里,首先就会在 /proc 下创建条目,较为简单,先来看 initexit

框架还是比较清晰的,需要深入源码来感受一下,第一部分代码:

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 结构。

此时,我们可以想象以下模型:

F3AFBf3.jpg!web

第二部分代码是:

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_zalloccache 中获取空间给 pde ,并且对条目名称进行检查。如果成功,则对名称、模式等属性赋值,设置引用计数并初始化锁。

__proc_register 接收两个参数,一个父亲 pde ,一个当前 pde ,目的是把当前 pde 挂到父亲名下,前面提到 subdir 的组织形式是红黑树,那么肯定涉及相关代码,来看:

首先判断当前 pdeid 是否越界,如果没有打开子目录锁,把当前 pdeparent 字段指向父亲 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

为什么这么做?内核文档给出了相关描述:

uAZVfmf.jpg!web

https://www.kernel.org/doc/Documentation/filesystems/seq_file.txt

你可能发现,内核文档里显示的是 seq_open ,而实验里是 single_open ,它们有什么区别呢?实际上内核文档的最后给出了答案:

J7fqiiq.jpg!web

谢宝友老师的实验中运用 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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK