命名空间——进程ID(pid)
一、PID命名空间的核心:“进程编号的平行世界”
PID命名空间的本质是让不同命名空间的进程拥有独立的PID编号体系,就像多个“平行世界”——每个世界里的进程编号(PID)可以重复,但彼此互不可见。
关键特性:
-
进程有两个PID:
一个进程在自己的命名空间内有一个“局部PID”(比如命名空间内的PID=1),在主机全局命名空间内还有一个“全局PID”(比如主机上的PID=1234)。
举例:容器内的bash进程,在容器的PID命名空间里是PID=1,但在主机上看可能是PID=5678。 -
嵌套结构:
PID命名空间可以像俄罗斯套娃一样嵌套。比如“父命名空间”创建“子命名空间”,子命名空间的进程能看到父命名空间的进程,但父命名空间看不到子命名空间的进程(除非显式配置)。 -
PID=1的特殊性:
每个PID命名空间的第一个进程会被分配PID=1,它的角色类似系统的init进程:- 负责回收该命名空间内的“僵尸进程”(如果它退出,内核会强制终止命名空间内所有进程);
- 这就是为什么在容器中直接杀掉
PID=1的进程,整个容器会立即退出(你可以试试docker exec进入容器后执行kill 1,容器会直接停止)。
二、实战:sudo unshare -fp --mount-proc 到底做了什么?
这个命令是创建并进入新PID命名空间的标准操作,我们一步步拆解:
1. sudo:获取权限
PID命名空间的创建需要root权限(普通用户受限制),所以用sudo提权。
2. -f(--fork):安全创建新进程
unshare默认会让“当前进程”进入新命名空间,但这样可能影响当前shell。-f会先fork一个新进程,让新进程进入新命名空间,当前shell不受影响(更安全)。
3. -p(--pid):创建新的PID命名空间
这是核心:告诉内核为新进程创建一个全新的PID命名空间。新命名空间内的进程将拥有独立的PID编号,看不到外部命名空间的进程。
4. --mount-proc:重新挂载/proc文件系统(最关键的一步!)
/proc是Linux的“进程信息虚拟文件系统”,里面的/proc/[PID]目录对应每个进程的详细信息。但/proc默认展示的是“当前进程所在命名空间”的进程信息。
如果不重新挂载/proc:
- 即使进入了新的PID命名空间,
/proc仍然指向原来命名空间的视图(比如主机的/proc); - 此时执行
ps或top,看到的还是主机的进程,无法体现PID命名空间的隔离效果。
--mount-proc的作用是:在新的PID命名空间中,自动将/proc重新挂载为“当前命名空间的进程视图”。这样执行ps时,只能看到新命名空间内的进程。
三、操作演示:直观感受隔离效果
步骤1:创建新PID命名空间并重新挂载/proc
# 执行命令,进入新的bash进程(在新PID命名空间中)
sudo unshare -fp --mount-proc /bin/bash
# 此时在新bash中查看PID:当前bash是这个命名空间的第一个进程,所以PID=1
echo $$ # 输出:1(局部PID)
# 查看进程列表:只能看到当前命名空间内的进程(只有bash自己)
ps aux # 输出中只有1个进程(PID=1的bash)
步骤2:在新命名空间中创建子进程
# 在新bash中启动一个sleep进程(子进程)
sleep 3600 & # 后台运行
# 查看进程:sleep的PID在当前命名空间中是2
ps aux # 输出:PID=1(bash)、PID=2(sleep)
步骤3:在主机终端查看(全局视角)
打开另一个终端(主机默认命名空间):
# 查找新命名空间中bash的全局PID(假设是1234)
pgrep -f "unshare -fp --mount-proc" # 输出:1234
# 查看该进程的子进程(sleep的全局PID,比如1235)
ps -ef | grep 1234 # 输出:1234(bash)、1235(sleep)
# 但主机终端中,sleep的PID是1235(全局PID),和新命名空间内的PID=2完全不同
总结
PID命名空间通过“独立的PID编号体系”实现进程隔离,而--mount-proc(或手动重新挂载/proc)是让这种隔离“可见”的关键(确保ps等工具只显示当前命名空间的进程)。
这正是容器技术中“进程隔离”的底层原理:每个容器就是一个独立的PID命名空间,容器内的PID=1进程(如nginx、bash)与主机的PID=1(systemd)完全隔离,彼此互不可见。
1084

被折叠的 条评论
为什么被折叠?



