前言
这是研一上学期的Linux内核分析作业。这个课程还是很有用的。
题目要求:
(1)能够返回指定进程(通过指定PID)的任务描述符;
(2)能够返回指定进程(通过指定PID)的进程地址空间的布局和统计信息(代码段、数据段、BSS段、堆、栈等区域的位置和大小、包含多少个虚拟内存区VMA、每个VMA的属性、该进程页表的地址、已映射的物理内存大小等。)(该题目需要研究Linux 进程描述符和内存描述符mm_struct。)
相关知识
系统调用是内核为用户进程提供服务的一种方式。通过系统调用,内核能够提供给用户模式下的进程和硬件设备的接口,保护对内核所管理的资源的访问,提高系统安全,提高程序的可移植性。
根据题目,本质就是要求在内核中设计实现一个新的函数,通过指定的进程ID可以返回该进程有关信息。
一个进程ID号,其实没有那么简单。因为Linux系统中有命名空间这种设定,目前实现的有六种不同的命名空间,分别为mount命名空间、UTS命名空间、IPC命名空间、用户命名空间、PID命名空间、网络命名空间。对于PID命名空间,有不同的层次,像图2这样。

在上图有四个命名空间,一个父命名空间衍生了两个子命名空间,其中的一个子命名空间又衍生了一个子命名空间。以PID命名空间为例,由于各个命名空间彼此隔离,所以每个命名空间都可以有 PID 号为 1 的进程;但又由于命名空间的层次性,父命名空间是知道子命名空间的存在,因此子命名空间要映射到父命名空间中去,因此上图中 level 1 中两个子命名空间的六个进程分别映射到其父命名空间的PID 号5~10。
命名空间增大了 PID 管理的复杂性,对于某些进程可能有多个PID——在其自身命名空间的PID以及其父命名空间的PID,凡能看到该进程的命名空间都会为其分配一个PID。因此就有:
全局ID:在内核本身和初始命名空间中唯一的ID,在系统启动期间开始的 init 进程即属于该初始命名空间。系统中每个进程都对应了该命名空间的一个PID,叫全局ID,保证在整个系统中唯一。
局部ID:对于属于某个特定的命名空间,它在其命名空间内分配的ID为局部ID,该ID也可以出现在其他的命名空间中。
所以根据题目,可以假设我们需要提供的是全局PID。
那么如何根据PID的数值找到task_struct结构体呢:
1.通过 PID 计算 pid 挂接到哈希表 pid_hash[] 的表项;
2. 遍历该表项,找到 pid 结构体中 nr 值与 PID 值相同的那个 pid
3.通过该 pid 结构体的 tasks 指针找到 node
4. 最后根据内核的 container_of 机制就能找到 task_struct 结构体
task_struct结构体包含了一个进程所需的所有信息。它定义在include/linux/sched.h文件中。
struct task_struct{
… …
struct mm_struct *mm;
pid