PID namespace用来隔离进程的PID空间,使得不同PID namespace中的进程号可以重复且互不影响。Linux下的每个进程都有一个对应的/proc/pid目录,该目录包含了进程相关的信息。对于一个PID namespace,/proc目录只包含当前namespace和它所有子孙后代namespace里的进程信息。创建一个新的PID namespace后,需要挂载/proc文件系统才能让子进程中top、ps等依赖/proc文件系统的命令正常工作。
1.命令行使用
下面通过例子演示挂载/proc文件系统的作用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| $ echo $$ 9501 $ readlink /proc/$$/ns/pid pid:[4026531836] $ sudo unshare --pid --mount --fork /bin/bash # readlink /proc/$$/ns/pid pid:[4026531836] # ps PID TTY TIME CMD 11178 pts/0 00:00:00 sudo 11180 pts/0 00:00:00 unshare 11183 pts/0 00:00:00 bash 11211 pts/0 00:00:00 ps # echo $$ 1 # ps 1 PID TTY STAT TIME COMMAND 1 ? Ss 0:02 /sbin/init splash
|
在创建新PID namespace后,当前bash进程的pid变成了1,但是通过ps命令可以看出进程信息使用的还是旧PID namespace中的,因此readlink读取的PID namespace信息是/sbin/init进程的,所以PID namespace没有变。–mount新建的mount namespace的挂载信息是从旧mount namespace中拷贝过来的,因此需要挂载新的/proc文件系统:
1 2 3 4 5 6 7
| # mount -t proc proc /proc # readlink /proc/$$/ns/pid pid:[4026532726] # ps PID TTY TIME CMD 1 pts/0 00:00:00 bash 26 pts/0 00:00:00 ps
|
现在看到的进程信息和PID namespace就是正确的了。其实unshare命令有一个专门的–mount-proc选项来配合PID namespace创建:
1
| $ sudo unshare --pid --mount-proc --fork /bin/bash
|
这样就会在创建PID和Mount namespace之后自动挂载/proc文件系统了。
2.代码实现
Mount namespace需要特权用户来执行,可以在运行时加sudo,或者可以先创建User namespace映射到root用户再运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| #include <sched.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <vector> #include <string> #include <sys/wait.h> #include <sys/mount.h> #include <sys/types.h>
int main() { int flags; int ret; flags = 0; flags |= CLONE_NEWNS; flags |= CLONE_NEWPID; if (unshare(flags) == -1) { perror("unshare"); exit(-1); }
pid_t cpid = fork(); if (cpid == -1) { perror("fork"); exit(-1); } if (cpid == 0) { if (mount("proc", "/proc", "proc", 0, NULL) == -1) { perror("mount"); exit(-1); } std::string cmd = "bash"; const char *argv[2] = {NULL}; argv[0] = cmd.c_str(); execvp("/bin/bash", (char* const*)argv); } int state; wait(&state); printf("child exit\n"); return 0; }
|
要用fork创建新进程,因为一个进程创建时所属的PID namespace就确定不变了,如果只用execvp创建的shell还是原进程,不能切换到新的PID namespace。
reference
https://www.cnblogs.com/sparkdev/p/9442208.html