如果大家有过在容器中执行ps命令的经验|docker核心之一pid命名空间的工作原理( 三 )


如果大家有过在容器中执行ps命令的经验|docker核心之一pid命名空间的工作原理
文章图片
首先到当前层次的命名空间申请一个pid出来 , 然后顺着命名空间的父节点 , 每一层也都要申请一个 , 并都记录到pid->numbers数组中 。
这里多说一下 , 如果pid申请失败的话 , 会报-ENOMEM错误 , 在用户层看起来就是“fork:无法分配内存” , 实际是由pid不足引起的 。 这个问题我在《明明还有大量内存 , 为啥报错“无法分配内存”?》提到过 。 2.3设置整数格式pid
当申请并构造完pid后 , 将其设置在task_struct上 , 记录起来 。 //file:kernel/fork.cstaticstructtask_struct*copy_process(){//2.2申请pidpid=alloc_pid(p-nsproxy-pid_ns);//2.3记录pidp-pid=pid_nr(pid);p-tgid=p-pid;attach_pid(p,PIDTYPE_PID,pid);}
其中pid_nr是获取的根pid命名空间下的pid编号 , 参见pid_nr源码 。 //file:include/linux/pid.hstaticinlinepid_tpid_nr(structpid*pid){pid_tnr=0;if(pid)nr=pid-numbers[0].nr;returnnr;}
然后再调用attach_pid是把申请到的pid结构挂到自己的pids[PIDTYPE_PID]链表里了 。 //file:kernel/pid.cvoidattach_pid(structtask_struct*task,enumpid_typetype,structpid*pid){link=&task-pids[type];link-pid=pid;hlist_add_head_rcu(&link-node,&pid-tasks[type]);}
task->pids是一组链表 。 三、容器进程pid查看
pid已经申请好了 , 那在容器中是如何查看当前层次的进程号的呢?比如我们在容器中看到的demo-ie进程的id就是1 。 #ps-efPIDUSERTIMECOMMAND1root0:00./demo-ie...
内核提供了个函数用来查看进程在当前某个命名空间的命名号 。 //file:kernel/pid.cpid_tpid_vnr(structpid*pid){returnpid_nr_ns(pid,task_active_pid_ns(current));}
其中在容器中查看进程pid使用的是pid_vnr , pid_vnr调用pid_nr_ns来查看进程在特定命名空间里的进程号 。
函数pid_nr_ns接收连个参数
第一个参数是进程里记录的pid对象(保存有在各个层次申请到的pid号)
第二个参数是指定的pid命名空间(通过task_active_pid_ns(current)获取) 。
当具备这两个参数后 , 就可以根据pid命名空间里记录的层次level取得容器进程的当前pid了//file:kernel/pid.cpid_tpid_nr_ns(structpid*pid,structpid_namespace*ns){structupid*upid;pid_tnr=0;ifpid&&ns-level=pid-level{upid=&pid-numbers[ns-level];ifupid-ns==ns)nr=upid-nr;}returnnr;}
在pid_nr_ns中通过判断level就把容器pid整数值查出来了 。 四、总结
最后 , 举个例子 , 假如有一个进程在level0级别的pid命名空间里申请到的进程号是1256 , 在level1容器pid命名空间里申请到的进程号是5 。 那么这个进程以及其pid在内存中的形式是下图这个样子的 。
如果大家有过在容器中执行ps命令的经验|docker核心之一pid命名空间的工作原理
文章图片
那么容器在查看进程的pid号的时候 , 传入容器的pid命名空间 , 就可以将该进程在容器中的pid号5给打印出来了!!