进程ID有类型之分
如果考虑进程之间有复杂的关系,如线程组、进程组、会话组,这些组均有组ID,分别为 TGID、PGID、SID,所以原来的 task_struct 中pid_link 指向一个 pid 结构体需要增加几项,用来指向到其组长的 pid 结构体,相应的 struct pid 原本只需要指回其 PID 所属进程的task_struct,现在要增加几项,用来链接那些以该 pid 为组长的所有进程组内进程。数据结构如下:
enum pid_type { PIDTYPE_PID, PIDTYPE_PGID, PIDTYPE_SID, PIDTYPE_MAX }; struct task_struct { //... pid_t pid; //PID pid_t tgid; //thread group id struct task_struct *group_leader; // threadgroup leader struct pid_link pids[PIDTYPE_MAX]; //... }; struct pid_link { struct hlist_node node; struct pid *pid; }; struct pid { struct hlist_head tasks[PIDTYPE_MAX]; int nr; //PID struct hlist_node pid_chain; // pid hash 散列表结点 };
上面 ID 的类型 PIDTYPE_MAX 表示 ID 类型数目。之所以不包括线程组ID,是因为内核中已经有指向到线程组的 task_struct 指针 group_leader,线程组 ID 无非就是 group_leader 的PID。
假如现在有三个进程A、B、C为同一个进程组,进程组长为A,这样的结构示意图如图3。
图3 增加ID类型的结构
关于上图有几点需要说明:
图中省去了 pid_hash 以及 pid_map 结构,因为第一种情况类似;
进程B和C的进程组组长为A,那么 pids[PIDTYPE_PGID] 的 pid 指针指向进程A的 pid 结构体;
进程A是进程B和C的组长,进程A的 pid 结构体的 tasks[PIDTYPE_PGID] 是一个散列表的头,它将所有以该pid 为组长的进程链接起来。
再次回顾本节的三个基本问题,在此结构上也很好去实现。
增加进程PID命名空间若在第二种情形下再增加PID命名空间,一个进程就可能有多个PID值了,因为在每一个可见的命名空间内都会分配一个PID,这样就需要改变 pid 的结构了,如下:
struct pid { unsigned int level; /* lists of tasks that use this pid */ struct hlist_head tasks[PIDTYPE_MAX]; struct upid numbers[1]; }; struct upid { int nr; struct pid_namespace *ns; struct hlist_node pid_chain; };
在 pid 结构体中增加了一个表示该进程所处的命名空间的层次level,以及一个可扩展的 upid 结构体。对于struct upid,表示在该命名空间所分配的进程的ID,ns指向是该ID所属的命名空间,pid_chain 表示在该命名空间的散列表。
举例来说,在level 2 的某个命名空间上新建了一个进程,分配给它的 pid 为45,映射到 level 1 的命名空间,分配给它的 pid 为 134;再映射到 level 0 的命名空间,分配给它的 pid 为289,对于这样的例子,如图4所示为其表示:
图4 增加PID命名空间之后的结构图
图中关于如果分配唯一的 PID 没有画出,但也是比较简单,与前面两种情形不同的是,这里分配唯一的 PID 是有命名空间的容器的,在PID命名空间内必须唯一,但各个命名空间之间不需要唯一。
至此,已经与 Linux 内核中数据结构相差不多了。