/*
* PID-map pages start out as NULL, they get allocated upon
* first use and are never deallocated. This way a low pid_max
* value does not cause lots of bitmaps to be allocated, but
* the scheme scales to up to 4 million PIDs, runtime.
*/
struct pid_namespace init_pid_ns = {
.kref = {
.refcount = ATOMIC_INIT(2),
},
.pidmap = {
[ 0 ... PIDMAP_ENTRIES-1] = { ATOMIC_INIT(BITS_PER_PAGE), NULL }
},
.last_pid = 0,
.nr_hashed = PIDNS_HASH_ADDING,
.level = 0,
.child_reaper = &init_task,
.user_ns = &init_user_ns,
.ns.inum = PROC_PID_INIT_INO,
#ifdef CONFIG_PID_NS
.ns.ops = &pidns_operations,
#endif
};
init_pid_ns的名字空间层级为0,没有父名字空间。pidmap被初始化为0,表示目前还没有id号被分配出去。last_pid设置为0,表示新分配的pid号将会从进程1开始。另外child_reaper在init_pid_ns被设置为init_task,该进程负责所有隶属于该命名空间进程死亡时的资源回收。init_task这个进程比较特殊,是linux内核启动时的进程1,是所有进程的祖先,在内核启动时它进行了一系列环境初始化操作:比如执行linux的启动脚本等。此外它负责初始名字空间里所有僵尸进程的资源回收。在这里init_pid_ns与子名字空间有一点区别:因为init_pid_ns是最初的名字空间,它是在内核加载完成之初建立的,因此它的child_reaper设定的init_task拥有系统环境初始化的作用;而后续通过clone建立的新名字空间,由于此时内核已经加载完成,因此新的名字空间的进程1是没有环境初始化作用的。后续将对子名字空间的进程1进行一些分析。
/*
* is_child_reaper returns true if the pid is the init process
* of the current namespace. As this one could be checked before
* pid_ns->child_reaper is assigned in copy_process, we check
* with the pid number.
*/
static struct task_struct *copy_process(...) {
...
if (is_child_reaper(pid)) { //当前进程在当前名字空间是进程1
//进程1负责回收当前名字空间的僵尸进程
ns_of_pid(pid)->child_reaper = p;
//设置为不可被杀死
p->signal->flags |= SIGNAL_UNKILLABLE;
}
...
}
可以看出,对于名字空间的进程1,内核将该进程赋值给该名字空间的child_reaper使之负责该名字空间的僵尸进程资源回收。同时将该进程设置为SIGNAL_UNKILLABLE,标志该进程不可被杀死。需要注意的是不可杀死这个属性只在本名字空间有效,对于其父名字空间来说该进程只是一个普通的进程,仍然可被杀死。与init_pid_ns不同的是,子名字空间的进程1并没有类似于init_task进程的环境初始化操作。因此当新建一个docker容器时,容器里的进程1并不会执行bashrc等初始化脚本。事实上,docker的一些设备初始化、环境变量初始化操作是由docker程序手动完成的,而不是由进程1完成的。