在那篇《使用独立PID namespace防止误杀进程》中的最后,我碰到了一个难题,那就是父PID namespace中的进程无法使用进入子PID namespace中通过echo $$ >$pidfile写入的pid值,进程发信号时,目标pid是和自己处于同样的PID namespace的。当时我的方法是使用ps+grep的方式去寻找,然而如果遇到多个同名进程的时候,这一招也将碰壁。那么有没有别的办法呢?在我这,这类问题是我最想碰到的啦,哈哈。
还记得进入子PID namespace的bash后,ps -e为何还是父进程的结果吗?因为mount proc的原因,因为在mount的时候,procfs就初始化了一个mount调用者的PID namespace,进入子PID namespace后,只要你不重新mount proc,那么/proc下挂载的还是原来的。proc下面有一个self目录,表示当前的进程,记住,self内部的status文件中获取到的值,使用的都是这个procfs被挂载时的PID namespace,也就是父PID namespace!于是办法就是:
echo $(cat /proc/self/status|awk -F ' ' '/PPid/{print $2}') >$pidfile
为何是PPid呢?因为Pid不是脚本的pid,而是cat程序的,由于cat是脚本调用的,自然取它的父进程pid就可以了,执行了上述的指令后,在mount新的procfs就好了。以下是一个测试脚本,在新的PID namespace中执行:
#!/bin/bash
echo $$
# 卸掉新的procfs,暴露出老的procfs
umount /proc
cat /proc/self/status|awk -F ' ' '/PPid/{print $2}'
# 重新挂载新的procfs
mount -t proc proc /proc
cat /proc/self/status|awk -F ' ' '/PPid/{print $2}'
其它的namespace
除了PID namespace,Linux中还有很多namespace,我比较关心的就是网络这一块,幸运的是,为了支持虚拟化以及隔离,Linux实现了netns,很简单,在调用clone的时候,加上CLONE_NEWNET这个flag就可以了,一个独立的netns,网卡,路由,iptables规则等都是隔离的,一块物理网卡只能属于一个namespace,使用下面的命令可以将一块网卡放到一个子namespace中:
ip link set dev ethX netns $子namespace在父namespace中的pid
这样做的一个典型应用就是管理接口,带外管理口可以放在一个独立的netns中,实现与其它业务网卡的完全隔离。