Linux内核Namespace隔离测试code

是什么?
linux的机制有点类似于数据库中的,可以为不同的进程提供各自的命名空间,命名空间互相隔离,进程跑在自己的中资源互相隔离 。
嵌入式进阶教程分门别类整理好了,看的时候十分方便,由于内容较多 , 这里就截取一部分图吧 。
需要的朋友私信【内核】即可领取 。
内核学习地址:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂
使用了的机制,将进程隔离在某个中,而在某一个命名空间内的进程可以感知到其他进程的存在,但是对空间外部的进程一无所知 。以一种轻量级的方式实现了虚拟化技术 。
提供了多种资源的隔离:
clone(…flag…)
所隔离的资源
根目录
IPC
V IPC,POSIX 消息队列
网络设备、协议栈、端口等
Mount
挂载点
PID
进程 ID
User
用户和组 ID
UTS
主机名和域名
多种类型的资源隔离可以让我们从文件系统开始,到进程通信、网络通信、用户权限管理、主机管理,一步步地实现各方面资源的隔离 , 使进程运行在一个虚拟化的环境中 。本文讨论的实现针对Linux内核3.8及其以后的版本 。
下面我们针对六种命名空间的API做一些实例讲解,亲身体验隔离的实现底层机制 。
2 实战
关于的系统调用主要有三个:
结合一段代码来介绍几个
#define _GNU_SOURCE#include #include #include #include #include #include#define STACK_SIZE (1024 * 1024)static char container_stack[STACK_SIZE]; char* const container_args[] = {"/bin/bash",NULL}; int container_main(void* arg){printf("Container - inside the container!n");execv(container_args[0], container_args);printf("Something's wrong!n");return 1;} int main(){printf("Parent - start a container!n");int container_pid = clone(container_main, container_stack+STACK_SIZE, SIGCHLD, NULL);waitpid(container_pid, NULL, 0);printf("Parent - container stopped!n");return 0;}
2.1 UTS
#define _GNU_SOURCE#include #include #include #include #include #include#define STACK_SIZE (1024 * 1024)static char container_stack[STACK_SIZE]; char* const container_args[] = {"/bin/bash",NULL}; int container_main(void* arg){printf("Container - inside the container!n");sethostname("container",10);execv(container_args[0], container_args);printf("Something's wrong!n");return 1;} int main(){printf("Parent - start a container!n");int container_pid = clone(container_main, container_stack+STACK_SIZE, SIGCHLD | CLONE_NEWUTS, NULL);waitpid(container_pid, NULL, 0);printf("Parent - container stopped!n");return 0;}
已经变了
[root@iZbp1d4tisi44j6vxze02fZ tmp]# vi c1.c[root@iZbp1d4tisi44j6vxze02fZ tmp]# gcc c1.c[root@iZbp1d4tisi44j6vxze02fZ tmp]# ./a.outParent - start a container!Container - inside the container![root@container tmp]# hostnamecontainer[root@container tmp]# uname -ncontainer[root@container tmp]# exitexitParent - container stopped![root@iZbp1d4tisi44j6vxze02fZ tmp]#
2.2 UTS
IPC全称 Inter- ,是Unix/Linux下进程间通信的一种方式,IPC有共享内存、信号量、消息队列等方法 。所以,为了隔离,我们也需要把IPC给隔离开来 , 这样,只有在同一个下的进程才能相互通信 。
如果你熟悉IPC的原理的话,你会知道,IPC需要有一个全局的ID,即然是全局的 , 那么就意味着我们的需要对这个ID隔离linux消息队列使用场景,不能让别的的进程看到 。
【Linux内核Namespace隔离测试code】#define _GNU_SOURCE#include #include #include #include #include #include#define STACK_SIZE (1024 * 1024)static char container_stack[STACK_SIZE]; char* const container_args[] = {"/bin/bash",NULL}; int container_main(void* arg){printf("Container - inside the container!n");sethostname("container",10);execv(container_args[0], container_args);printf("Something's wrong!n");return 1;} int main(){printf("Parent - start a container!n");int container_pid = clone(container_main, container_stack+STACK_SIZE,SIGCHLD | CLONE_NEWUTS | CLONE_NEWIPC, NULL);waitpid(container_pid, NULL, 0);printf("Parent - container stopped!n");return 0;}
ipcs用法 ipcs -a 默认的输出信息 打印出当前系统中所有的进程间通信方式的信息 ipcs -m 打印出使用共享内存进行进程间通信的信息 ipcs -q 打印出使用消息队列进行进程间通信的信息 ipcs -s 打印出使用信号进行进程间通信的信息
创建查询ipcs
[root@iZbp1d4tisi44j6vxze02fZ tmp]# ipcmk -QMessage queue id: 0[root@iZbp1d4tisi44j6vxze02fZ tmp]# ipcs -q------ Message Queues --------keymsqidownerpermsused-bytesmessages0x3b786aa7 0root64400
查看所有ipcs
[root@iZbp1d4tisi44j6vxze02fZ tmp]# ipcs -a------ Message Queues --------keymsqidownerpermsused-bytesmessages0x3b786aa7 0root64400------ Shared Memory Segments --------keyshmidownerpermsbytesnattchstatus0x00000000 65536root6005242882dest0x00000000 229377root6005242882dest0x00000000 7143426root600167772162dest0x00802c81 400392195mingjie.gm 6005660x00000000 491524root6005242882dest0x00000000 212926469mingjie.gm 64029286401640x00000000 212959238mingjie.gm 6401677721600 820x00000000 884743root6005242882dest0x00000000 212992008mingjie.gm 64013848576820xb4ce0a78 213024777mingjie.gm 64012288820x00803069 388169738mingjie.gm 600566#define _GNU_SOURCE------ Semaphore Arrays --------keysemidownerpermsnsems0x671cfa8c 6422528mingjie.gm 6401520x671cfa8d 6455297mingjie.gm 6401520x671cfa8e 6488066mingjie.gm 640152
执行程序
[root@iZbp1d4tisi44j6vxze02fZ tmp]# gcc c2.c -o ipc[root@iZbp1d4tisi44j6vxze02fZ tmp]# ./ipcParent - start a container!Container - inside the container![root@container tmp]#[root@container tmp]# ipcs -a------ Message Queues --------keymsqidownerpermsused-bytesmessages------ Shared Memory Segments --------keyshmidownerpermsbytesnattchstatus------ Semaphore Arrays --------keysemidownerpermsnsems
IPC已经被隔离了!
2.3 PID
#define _GNU_SOURCE#include #include #include #include #include #include#define STACK_SIZE (1024 * 1024)static char container_stack[STACK_SIZE]; char* const container_args[] = {"/bin/bash",NULL}; int container_main(void* arg){printf("Container [%5d] - inside the container!n", getpid());sethostname("container",10);execv(container_args[0], container_args);printf("Something's wrong!n");return 1;} int main(){printf("Parent - start a container!n");int container_pid = clone(container_main, container_stack+STACK_SIZE,SIGCHLD | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID, NULL);waitpid(container_pid, NULL, 0);printf("Parent - container stopped!n");return 0;}
运行结果如下(我们可以看到,子进程的pid是1了):
[root@iZbp1d4tisi44j6vxze02fZ tmp]# gcc c3.c -o pid[root@iZbp1d4tisi44j6vxze02fZ tmp]# ./pidParent - start a container!Container [1] - inside the container![root@container tmp]# echo $
你可能会问,PID为1有个毛用?。课颐侵??nbsp;, 在传统的UNIX系统中,PID为1的进程是init,地位非常特殊 。他作为所有进程的父进程,有很多特权(比如:屏蔽信号等),另外 , 其还会检查所有进程的状态,我们知道 , 如果某个子进程脱离了父进程(父进程没有wait它),那么init就会负责回收资源并结束这个子进程 。所以,要做到进程空间的隔离 , 首先要创建出PID为1的进程,最好就像那样,把子进程的PID在容器内变成1 。
但是 , 我们会发现linux消息队列使用场景,在子进程的shell里输入ps,top等命令,我们还是可以看得到所有进程 。说明并没有完全隔离 。这是因为,像ps, top这些命令会去读/proc文件系统,所以 , 因为/proc文件系统在父进程和子进程都是一样的,所以这些命令显示的东西都是一样的 。
所以 , 我们还需要对文件系统进行隔离 。
2.4 Mount
下面的例程中,我们在启用了mount 并在子进程中重新mount了/proc文件系统 。
#define _GNU_SOURCE#include #include #include #include #include #include#define STACK_SIZE (1024 * 1024)static char container_stack[STACK_SIZE]; char* const container_args[] = {"/bin/bash",NULL}; int container_main(void* arg){printf("Container [%5d] - inside the container!n", getpid());sethostname("container",10);/* 重新mount proc文件系统到 /proc下 */system("mount -t proc proc /proc");execv(container_args[0], container_args);printf("Something's wrong!n");return 1;} int main(){printf("Parent [%5d] - start a container!n", getpid());int container_pid = clone(container_main, container_stack+STACK_SIZE,SIGCHLD | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNS, NULL);waitpid(container_pid, NULL, 0);printf("Parent - container stopped!n");return 0;}
运行结果如下:
[root@iZbp1d4tisi44j6vxze02fZ tmp]# gcc c4.c[root@iZbp1d4tisi44j6vxze02fZ tmp]# ./a.outParent [16901] - start a container!Container [1] - inside the container![root@container tmp]# ps -elfF S UIDPIDPPIDC PRINI ADDR SZ WCHANSTIME TTYTIME CMD4 S root100800 - 29185 do_wai 20:04 pts/300:00:00 /bin/bash0 R root3510800 - 38833 -20:05 pts/300:00:00 ps -elf
在通过创建mount 后,父进程会把自己的文件结构复制给子进程中 。而子进程中新的中的所有mount操作都只影响自身的文件系统,而不对外界产生任何影响 。这样可以做到比较严格地隔离 。
上面的代码是在子进程中重新mount了一次,否则还是会看到父进程的proc目录!
2.5 User
User 主要是用了的参数 。使用了这个参数后,内部看到的UID和GID已经与外部不同了,默认显示为65534 。那是因为容器找不到其真正的UID所以,设置上了最大的UID(其设置定义在/proc/sys//) 。
要把容器中的uid和真实系统的uid给映射在一起,需要修改 /proc// 和 /proc// 这两个文件 。这两个文件的格式为: ID–ns ID–ns
其中:
$ cat /proc/2465/uid_map010001
再比如下面的示例:表示把内部从0开始的uid映射到外部从0开始的uid , 其最大范围是无符号32位整形
$ cat /proc/$/uid_map004294967295
User 是以普通用户运行,但是别的需要root权限 , 那么,如果我要同时使用多个 , 该怎么办呢?一般来说,我们先用一般用户创建User,然后把这个一般用户映射成root,在容器内用root来创建其它的 。
2.6
2.7 文件
同一个容器的进程在一个NS下 。
本文到此结束,希望对大家有所帮助 。