task_struct 中有这么几个和进程id有关的字段
- pid_t pid;
-
pid_t tgid;
-
....
-
struct pid_link pids[pidtype_max];
pid和tgid是表示该进程的全局进程id的,前者是当前进程的id号,后者是当前进程所在线程组的线程组id。
而后者则是指向pid哈希表的链接,里面存储了命名空间相关的信息。
1、进程命名空间
include/linux/pid_namespace.h里有pid_namespace的定义:
- struct pid_namespace {
-
struct kref kref;
-
struct pidmap pidmap[pidmap_entries];
-
int last_pid;
-
struct task_struct *child_reaper;
-
struct kmem_cache *pid_cachep;
-
unsigned int level;
-
struct pid_namespace *parent;
-
#ifdef config_proc_fs
-
struct vfsmount *proc_mnt;
-
#endif
-
#ifdef config_bsd_process_acct
-
struct bsd_acct_struct *bacct;
-
#endif
-
};
我们这里只关心其中的child_reaper,level和parent这三个字段
child_reaper:指向的是当前命名空间的init进程,每个命名空间都有一个作用相当于全局init进程的进程。
level:代表当前命名空间的等级,初始命名空间的level为0,它的子命名空间level为1,依次递增,而且子命名空间对父命名空间是可见的。
parent:指向父命名空间的指针
因此命名空间的结构如下所示:
2、upid
正是命名空间的作用,使得进程能够分配到不同的命名空间中去,而且进程在自己所在的命名空间中有着局部的pid,这就是局部pid,因此各个命名空间的pid有可能重复,也即是一个pid可能为多个进程使用,在pid.h文件中定义了一个用来关联pid和命名空间的结构体.
- struct upid {
-
/* try to keep pid_chain in the same cacheline as nr for find_vpid */
-
int nr;
-
struct pid_namespace *ns;
-
struct hlist_node pid_chain;
-
};
其中nr表示id具体的值;
ns是指向命名空间的指针;
pid_chain是指向pid哈希列表的指针,用于关联对于的pid
3、pid
在pid中又对使用该pid的task进行了关联
- struct pid
-
{
-
atomic_t count;
-
unsigned int level;
-
/* lists of tasks that use this pid */
-
struct hlist_head tasks[pidtype_max];
-
struct rcu_head rcu;
-
struct upid numbers[1];
-
};
count 是指使用该pid的task的数目;
level 表示可以看到该pid的命名空间的数目,也就是包含该进程的命名空间的深度
tasks[pidtype_max]是一个数组,每个数组项都是一个散列表头,分别对应以下三种类型:
- enum pid_type
-
{
-
pidtype_pid,
-
pidtype_pgid,
-
pidtype_sid,
-
pidtype_max
-
};
numbers[1]是一个upid的实例数组,每个数组项代表一个命名空间,用来表示一个pid可以属于不同的命名空间,该元素放在末尾,可以向数组添加附加的项。
总结
1、多个task_struct可以共用一个pid
2、一个pid可以属于不同的命名空间
那么最终,linux下进程命名空间和进程的关系结构如下:
可以看到,多个task_struct指向一个pid,同时pid的hash数组里安装不同的类型对task进行散列,并且一个pid会属于多个命名空间。
参考资料:
1、《深入linux内核架构》
2、
阅读(7044) | 评论(0) | 转发(2) |