PID命名空间隔离的意义
PID 命名空间的隔离作用非常关键,主要体现在以下几个方面:
- 进程树的独立性:每个容器或命名空间有独立的进程 ID,从 1 开始,确保进程间互不干扰。
- 安全性和隔离性:通过进程 PID 隔离,确保容器和宿主机之间的进程管理不会发生冲突,从而提升容器环境的安全性。
- 简化容器管理和监控:容器中的进程管理完全独立,使得容器可以像虚拟机一样独立进行监控、管理和生命周期管理。
- 实现虚拟化资源隔离:PID 命名空间为容器化、虚拟化环境提供进程层面的资源隔离,使得多个容器或虚拟机能够共享同一台物理机器而不互相干扰。
综上所述,PID 命名空间是实现 容器化和虚拟化 环境中 进程隔离 和 资源管理 的基础,它为每个容器或虚拟机提供了独立的进程树和 PID 管理,极大增强了系统的安全性和资源利用效率。
进程的pid_namespace结构
进程创建
进程创建时函数调用栈如下:
根据flags判断是否要创建新的namespace,如果不需要,就直接copy父进程的namespace,也就是父进程的task中的nsproxy成员:
……
看一下get_nsproxy函数,就是简单地将父进程的nsproxy引用计数加一:
如果需要创建新的namespace则要调用create_new_namespace函数,首先创建一个新的namespace叫做new_nsp:
new_nsp的pid_ns_for_children成员通过调用copy_pid_ns来获得:
下面看这个函数,直接看create_pid_namespace:
create_pid_namespace函数如下所示,首先分配一个空的ns,然后ns的level就是父进程ns的level+1,然后引用计数则设定为1:
因此新建namespace过程如下:
getpid过程分析
https://elixir.bootlin.com/linux/v6.13.5/source/kernel/sys.c
先进性源码分析,一路跟进:
可以看到前面的task_pid_ptr和rcu_dereference应该就是从task中得到当前进程的pid结构体,
其过程图示如下:
struct pid到底是干什么的
struct pid的定义如下:https://elixir.bootlin.com/linux/v6.13.5/source/include/linux/pid.h#L55
upid定义如下:
看一下何时分配它的,首先定位到copy_process函数中,https://elixir.bootlin.com/linux/v6.13.5/source/kernel/fork.c#L2149,其参数会传入一个pid(经过查看内核源码中仅有的三处引用可以发现,有一处为init_struct_pid,其余两处都是NULL):
之后会有这样的处理:
让我们跟进到alloc_pid函数中https://elixir.bootlin.com/linux/v6.13.5/source/kernel/pid.c#L166,首先是分配空间并设置level:
之后是一个for循环:
在for循环中会计算出一个nr,似乎是在每一层的pid_namespace中给当前进程分配一个pid:
然后在pid的numbers数组中设置:
退出for循环之后,设置引用计数为1,然后倒序遍历pid的numbers数组替换nr?:
回到copy_process函数后设置task的pid为pid_nr(pid):