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 也能看到隔离后的结果。

浙公网安备 33010602011771号