linux下命名空间是一种环境隔离的方法,基于内核态,通过命名空间可以将一些进程抽象到不同的容器中,使得各个容器彼此隔离,但是隔离之后相互间能否存在一些共享,就看你创建namespace的选项了
目前namespaces官方有如下种类:
Mount namespaces CLONE_NEWNS UTS namespaces CLONE_NEWUTS IPC namespaces CLONE_NEWIPC PID namespaces CLONE_NEWPID Network namespaces CLONE_NEWNET User namespaces CLONE_NEWUSER
namespace的层次关系,也可以通过父namespace和子namespace来说明,比如对于PID namespace:
1: 父namespace有pid为1,2,3,4,5这五个进程,衍生出两个子namespace,A和B
2: pid为2和3的进程分给了子namespace A,pid为5的进程分给了子namespace B
3:namespace的作用是相互隔离,也就是此时A和B是互不知情
4:关键在A里,pid可就不一定是2和3了,说不定就是1和2了,同理在B里,pid可能就是1了,为什么一定要带个1呢,pid为1是init进程,人家牛逼,linux系统的管家,既然都隔离成单独的容器,理应先分别创建一个pid为1的进程,也就是说这里父namespace里的pid为2和3两个进程分到A里,进程pid变成了1和2,pid为5的进程分到B里,进程pid变成了1
5:父namespace是知道两个子namespace的存在的,虽然它们两互相不清楚,所以它们两都会映射到父namespace,A的两个进程映射父namespace的2和3,B的一个进程映射父namespace的5,始终记住namespace仅仅是环境隔离的抽象,进程依旧是5个
下面是网上的一个小例子,修改了几个地方
#define _GNU_SOURCE #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <sched.h> #include <signal.h> #include <stdlib.h> #define STACK_SIZE (1024 * 1024) static char container_stack[STACK_SIZE]; char* const container_args[] = {"/bin/bash", NULL }; int container(){ printf("Container pid: %d\n", getpid()); sethostname("container", 10); execv(container_args[0], container_args); printf("Something wrong!\n"); return 1; } void main(){ printf("Parent %d: start a container\n", getpid()); int container_pid; container_pid = clone(container, container_stack + STACK_SIZE, CLONE_NEWUTS | CLONE_NEWPID | SIGCHLD, NULL); waitpid(container_pid, NULL, 0); printf("Parent: container stopped!\n"); }
记得第一行一定要,否则就回不识别clone的标志位CLONE_NEWPID,而这个标志位才是创建新的namespace,这里clone创建一个新的进程,并且是新的namespace,就像一个新的操作系统一样,同时创建pid,当然是从1开始,所以container函数里的printf打印的就是新进程的pid,等下瞄瞄是多少,除此之外,新的进程里还重新设置了hostname;main函数里printf打印的就是原主进程的pid
root@2015-vm1:/home/lihui# cc hello.c root@2015-vm1:/home/lihui# ./a.out Parent 6775: start a container Container pid: 1 root@container:/home/lihui#
父进程就不看了,子进程在新的容器里pid变成了1,真的相当于重新创建了一个操作系统一样,同时可以看到hostname也变成了container了
但是随便试验一下,两个namespace的隔离性,top命令却还能够看到所有进程的信息,原因是执行这个命令回去读/proc文件系统,所以只要将这个文件系统也给隔离了应该就行了,这里就需要用到另一种namespace的标志位,然后子进程里重新mount一次
#define _GNU_SOURCE #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <sched.h> #include <signal.h> #include <stdlib.h> #define STACK_SIZE (1024 * 1024) static char container_stack[STACK_SIZE]; char* const container_args[] = {"/bin/bash", NULL }; int container(){ printf("Container pid: %d\n", getpid()); sethostname("container", 10); system("mount -t proc proc /proc"); execv(container_args[0], container_args); printf("Something wrong!\n"); return 1; } void main(){ printf("Parent %d: start a container\n", getpid()); int container_pid; container_pid = clone(container, container_stack + STACK_SIZE, CLONE_NEWUTS | CLONE_NEWPID | SIGCHLD | CLONE_NEWNS, NULL); waitpid(container_pid, NULL, 0); printf("Parent: container stopped!\n"); }
clone多加了一个CLONE_NEWNS标志位,然后在子进程里重新mount一次,这样,就对文件系统/proc进行了隔离,然后子进程容器里查看top结果
root@2015-vm1:/home/lihui# cc hello.c root@2015-vm1:/home/lihui# ./a.out Parent 7141: start a container Container pid: 1 root@container:/home/lihui# top Tasks: 2 total, 1 running, 1 sleeping, 0 stopped, 0 zombie %Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem: 1003252 total, 405100 used, 598152 free, 25236 buffers KiB Swap: 0 total, 0 used, 0 free. 211868 cached Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1 root 20 0 19792 3436 3120 S 0.0 0.3 0:00.00 bash 13 root 20 0 23544 2860 2508 R 0.0 0.3 0:00.00 top
这样就只看到了top和execv这两个进程了