Loading

PID 隔离(进程ID隔离)


一、本节目标

在已有的 UTS 隔离和 IPC 隔离基础上,继续为 clone 新增系统调用参数 CLONE_NEWPID,实现 PID 资源的隔离


二、先验证不隔离 PID 的效果

退出所有终端,重新连接宿主机,开启两个终端窗口:一边是宿主机环境,一边是容器环境。

2.1 确认之前的隔离依然生效

用上一节的 test3(只有 UTS + IPC 隔离)进入容器,确认之前的隔离正常:

容器终端:

[root@test04 container_test]# ./test3 
Parent - start a container!
Container - inside the container!

[root@container container_test]# hostname
container

[root@container container_test]# ipcs -q

--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息      

主机名隔离 ✅,IPC 隔离 ✅(容器内看不到宿主机的消息队列)。

2.2 查看容器进程在宿主机上的 PID

容器启动后执行的第一个进程是 /bin/bash。无论容器里起多少个进程,它们本质都运行在宿主机上,所以在宿主机上一定能看到。

宿主机终端:

[root@test04 ~]# ps aux | grep '/bin/bash' | grep -v grep
root       31715  0.0  0.1 116604  2988 pts/1    S+   15:51   0:00 /bin/bash

容器的第一个进程在宿主机上的 PID 是 31715

容器终端:

[root@container container_test]# echo $$
31715

容器内看到的 PID 也是 31715,两者完全一样 —— 说明 PID 目前没有隔离,容器和宿主机共享同一套 PID 编号。

2.3 补充:容器第一个进程的父进程是谁?

宿主机终端:

[root@test04 ~]# ps -elf | grep '/bin/bash' | grep -v grep
4 S root       31715   31714  0  80   0 - 29151 n_tty_ 15:51 pts/1    00:00:00 /bin/bash

[root@test04 ~]# ps aux | grep test3 | grep -v grep
root       31714  0.0  0.0   5240   348 pts/1    S    15:51   0:00 ./test3

容器第一个进程(PID 31715)的父进程是 PID 31714,正是 test3 —— 也就是我们手动实现的容器引擎。容器引擎是容器第一个进程的父进程,这个关系很好理解:是容器引擎通过 clone 创建出的容器进程。


三、添加 CLONE_NEWPID 实现隔离

3.1 代码修改

退出容器,编辑代码,在 clone 调用中加上 CLONE_NEWPID

int container_pid = clone(container_main, container_stack+STACK_SIZE, CLONE_NEWPID | CLONE_NEWIPC | CLONE_NEWUTS | SIGCHLD, NULL);

3.2 编译运行

[root@test04 container_test]# gcc -o test4 test.c
[root@test04 container_test]# ./test4
Parent - start a container!
Container - inside the container!

3.3 验证 PID 隔离效果

宿主机终端 —— 查看容器进程的 PID:

[root@test04 ~]# ps aux | grep '/bin/bash' | grep -v grep
root       33126  0.0  0.1 116480  2816 pts/1    S+   15:59   0:00 /bin/bash

宿主机上看到容器第一个进程的 PID 是 33126

容器终端 —— 查看自己的 PID:

[root@container container_test]# echo $$
1

容器内看到自己的 PID 是 1(一号进程)!宿主机上也有一号进程(CentOS 7 是 systemd,CentOS 6 是 init),但两者不冲突。同一个进程,宿主机上看到的 PID 是 33126,容器内看到的 PID 是 1 —— PID 隔离成功。

3.4 进一步验证:在容器内启动新进程

在容器内创建一个脚本,启动时打印自己的 PID:

容器终端:

[root@container container_test]# vim a.sh
  
echo $$
sleep 10000

[root@container container_test]# sh a.sh &
[1] 21

容器内看到脚本进程的 PID 是 21

宿主机终端:

[root@test04 ~]# ps aux | grep 'a.sh' | grep -v grep
root       36896  0.0  0.0 113284  1196 pts/1    S    16:18   0:00 sh a.sh

宿主机上看到同一个脚本进程的 PID 是 36896。容器内是 21,宿主机上是 36896,完全不同 —— 再次证明 PID 隔离已经生效。


四、一个容易产生困惑的点:ps aux 的结果

4.1 现象

在容器内执行 ps aux 查看进程:

[root@container container_test]# ps aux | grep 'a.sh' | grep -v grep
root       36896  0.0  0.0 113284  1196 pts/1    S    16:18   0:00 sh a.sh

发现 ps aux 显示的 PID(36896)跟宿主机上看到的一模一样!这不是说好了隔离吗?

4.2 原因解释

不要怀疑,PID 确实已经隔离了。 之前用 echo $$ 验证的结果(容器内 21 vs 宿主机 36896)没有骗你。

在容器里执行 ps aux 看到的结果跟宿主机一样,原因是:ps aux 这条命令查看的信息来自于 /proc 这个虚拟文件系统。而目前我们并没有做到容器内的 /proc 目录与宿主机隔离,容器内的 /proc 和宿主机的 /proc 是同一个,所以 ps aux 看到的信息当然一样。

一定要区分清楚:

验证方式 原理 结果
echo $$ 直接打印进程本身的 PID 号 容器内和宿主机不同(已隔离)
ps aux 读取 /proc 虚拟文件系统的内容 容器内和宿主机相同/proc 未隔离)

五、本节小结与下节预告

要素 内容
隔离的资源 PID(进程ID)
系统调用参数 CLONE_NEWPID
验证方式 echo $$ 容器内显示 1,宿主机显示 33126,互不冲突
注意点 ps aux 读取的是 /proc,目前 /proc 未隔离,所以 ps aux 结果与宿主机相同,但不代表 PID 没隔离

截至目前已累计隔离的资源:

参数 隔离的资源
CLONE_NEWUTS 主机名 ✅
CLONE_NEWIPC IPC 通信工具 ✅
CLONE_NEWPID PID 进程ID ✅

下一节的目标:让容器单独挂载自己的 /proc 虚拟文件系统,与宿主机隔离开,这样在容器内用 ps aux 也能看到隔离后的结果。

posted @ 2026-06-19 09:42  知杏  阅读(6)  评论(0)    收藏  举报