linux容器基础-namespace-3(pid)

pid namespace

pid namespace表示隔离一个具有独立PID的运行环境。
在每一个pid namespace中,进程的pid都从1开始,且和其他pid namespace中的PID互不影响。
这意味着,不同pid namespace中可以有相同的PID值。

在介绍pid namespace之前,先创建其他类型的namespace然后查看进程关系:

#在root namesapce查看当前进程的id是4048028
root@ubuntu-server:~# echo $$
4048028

root@ubuntu-server:~# pstree -p |grep 4048028
|-sshd(3944519)---sshd(4047935)---bash(4048028)-+-grep(4074182)

#进程的关系:
#systemd(1) |-sshd(3944519 这个是sshd服务server)|-sshd(4047935,这个是ssh连接的进程)|---bash(4048028)-+-grep(4074182)

root@ubuntu-server:~# ps -ef|grep 3944519
root     3944519       1  0 Jan15 ?        00:00:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups

创建一个uts namespace

root@ubuntu-server:~# unshare -u /bin/bash

在uts namespace中查看当前进程

root@ubuntu-server:~# pstree -p |grep grep
|-sshd(3944519)---sshd(4047935)---bash(4048028)---bash(4074716)-+-grep(4074764)

root@ubuntu-server:# ls -l /proc/4074716/ns|grep uts
lrwxrwxrwx 1 root root 0 Jan 16 15:07 uts -> uts:[4026532372]
root@ubuntu-server:# ls -l /proc/4048028/ns|grep uts
lrwxrwxrwx 1 root root 0 Jan 16 11:17 uts -> uts:[4026531838]
root@ubuntu-server:~# ls -l /proc/4047935/ns|grep uts
lrwxrwxrwx 1 root root 0 Jan 16 11:17 uts -> uts:[4026531838]

#从上面输出的结果可知,创建uts类型namespace时,unshare进程会在创建新的namespace后被该namespace中的第一个进程给替换掉。
#并且我们可以看到unshare创建的bash(4074716)进程与其父进程(4048028),2者的uts namespace是不一致的。

创建pid namespace:

unshare --pid --fork [--mount-proc] <CMD>
--pid(-p):    表示创建pid namespace
--mount-proc: 表示创建pid namespace时重新挂载procfs
--fork(-f):   表示创建pid namespace时,不是直接替换unshare进程,而是fork unshare进程,并使用CMD替换fork出来的子进程

我们使用fork的参数来验证一下:

root@ubuntu-server:~# echo $$
4048028

root@ubuntu-server:# unshare -p -f  --mount-proc /bin/bash
root@ubuntu-server:# pstree -p |grep grep
bash(1)-+-grep(9)

#终端2,重新打开一个终端
root@ubuntu-server:~# pstree -p 4048028
bash(4048028)───unshare(4090558)───bash(4090559)

root@ubuntu-server:# ls -l /proc/4048028/ns/pid
lrwxrwxrwx 1 root root 0 Jan 16 11:17 /proc/4048028/ns/pid -> 'pid:[4026531836]'
root@ubuntu-server:# ls -l /proc/4090558/ns/pid
lrwxrwxrwx 1 root root 0 Jan 16 17:27 /proc/4090558/ns/pid -> 'pid:[4026531836]'
root@ubuntu-server:~# ls -l /proc/4090559/ns/pid
lrwxrwxrwx 1 root root 0 Jan 16 17:27 /proc/4090559/ns/pid -> 'pid:[4026532379]'

#通过这个实验,我们可以看到unshare(4090558)这个进程还是在原来的namespace,并不会加入新的namespace,
而fork出来的bash(4090559)进程,他是加入到了一个新的namespace当中。

嵌套pid namespace

这个很容易理解,就是不同namespace之间可以相互嵌套,所有的子孙pid namespace中的进程信息都会保存在父级以及祖先级namespace中,
只不过在不同嵌套层级中,同一个进程对应的PID不同。

#我们这里在namespace1之中在创建一个namespace2看看进程的关系是什么样的
#------第一个终端------
#namespace1
root@ubuntu-server:~# unshare -p -f -u --mount-proc /bin/bash
root@ubuntu-server:~# hostname namespace1
root@ubuntu-server:~# exec bash

#在namespace1中创建namespace2
root@namespace1:~# unshare -p -f -u --mount-proc /bin/bash
root@namespace1:~# hostname namespace2
root@namespace1:~# exec bash
root@namespace2:~# pstree -p |grep grep
bash(1)-+-grep(16)

##------第二个终端------,也就是root namespace下面
root@ubuntu-server:~# pstree -p 4048028
bash(4048028)───unshare(4097920)───bash(4097921)───unshare(4098083)───bash(4098084)

#在上面fork测试中我们知道unshare进程还在原来的namespace,而unshare创建的bash是在新的namespace中。
所以bash(4048028)───unshare(4097920)在同一个ns,bash(4097921)───unshare(4098083)在同一个ns,bash(4098084)在一个ns

root@ubuntu-server:~# ls -l -h  /proc/4048028/ns/pid
lrwxrwxrwx 1 root root 0 Jan 16 11:17 /proc/4048028/ns/pid -> 'pid:[4026531836]'
root@ubuntu-server:~# ls -l -h  /proc/4097920/ns/pid
lrwxrwxrwx 1 root root 0 Jan 16 18:31 /proc/4097920/ns/pid -> 'pid:[4026531836]'
root@ubuntu-server:~# ls -l -h  /proc/4097921/ns/pid
lrwxrwxrwx 1 root root 0 Jan 16 18:31 /proc/4097921/ns/pid -> 'pid:[4026532386]'
root@ubuntu-server:~# ls -l -h  /proc/4098083/ns/pid
lrwxrwxrwx 1 root root 0 Jan 16 18:31 /proc/4098083/ns/pid -> 'pid:[4026532386]'
root@ubuntu-server:~# ls -l -h  /proc/4098084/ns/pid
lrwxrwxrwx 1 root root 0 Jan 16 18:31 /proc/4098084/ns/pid -> 'pid:[4026532391]'

pid namespace和procfs

/proc目录是内核对外暴露的可供用户查看或修改的内核中所记录的信息,包括内核自身的部分信息以及每个进程的信息。
比如对于pid=N的进程来说,它的信息保存在/proc/目录下。在操作系统启动的过程中,会挂载procfs到/proc目录,它存在于root namespace中。
但是,创建新的pid namespace时不会自动重新挂载procfs,而是直接拷贝父级namespace的挂载点信息。
这使得在新的pid namespace中仍然保留了父级namespace的/proc目录,也就是在新创建的这个pid namespace中仍然保留了父级的进程信息。

#不挂载 --mount-proc此时namespace还是继承宿主机的进程树
root@ubuntu-server:~# unshare -p -u -f /bin/bash
root@ubuntu-server:~# hostname namepsace1
root@ubuntu-server:~# exec bash
root@namepsace1:~# pstree
systemd─┬─ModemManager───2*[{ModemManager}]
        ├─agetty
        ├─cron
        ├─dbus-daemon
        ├─dnsmasq───dnsmasq
        ├─irqbalance───{irqbalance}
        ......

之所以有上述问题,其原因是在pid namespace中保留了root namespace中的/proc目录,而不是属于pid namespace自己的/proc。
但用户创建pid namespace时希望的是有完全独立的进程运行环境。
这时,需要在pid namespace中重新挂载procfs,或者在创建pid namespace时指定–mount-proc选项。

# 注意这儿添加了mount-proc参数
root@ubuntu-server:~# unshare -p -u -f --mount-proc /bin/bash
root@ubuntu-server:~# hostname namespace1
root@ubuntu-server:~# exec bash

# 进程树中展示了bash 为init进程
[root@namespace1 ~]# pstree -p
bash(1)───pstree(24)

pid namespace信号量

pid=1的进程是每一个pid namespace的核心进程(init进程),它不仅负责收养其所在pid namespace中的孤儿进程,还影响整个pid namespace。

当pid namespace中pid=1的进程退出或终止,内核默认会发送SIGKILL信号给该pid namespace中的所有进程以便杀掉它们(如果该pid namespace中有子孙namespace,也会直接被杀)。

在创建pid namespace时可以通过–kill-child选项指定pid=1的进程终止后内核要发送给pid namespace中进程的信号,其默认信号便是SIGKILL。

root@ubuntu-server:~# lsns
4026532379 pid         1 4099754 root             bash

root@ubuntu-server:~# kill -9 4099754
#在namespace1的进程,kill掉这个进程后这个namespace1整个namespace都被干掉了
root@namepsace1:~# Killed

#也可以指定为其他信号
unshare -p -f -m -u --mount-proc --kill-child=SIGHUP /bin/bash

我们在这里通过一些简单的例子来熟悉了一下pid namespace是如何工作的,当以后我们在玩k8s和docker的时候看到容器中的进程
是如何与宿主机隔离的就不会感到疑惑了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值