是什么?
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消息队列使用场景,不能让别的的进程看到。
#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 --------key msqid owner perms used-bytes messages0x3b786aa7 0 root 644 0 0
查看所有ipcs
[root@iZbp1d4tisi44j6vxze02fZ tmp]# ipcs -a------ Message Queues --------key msqid owner perms used-bytes messages0x3b786aa7 0 root 644 0 0------ Shared Memory Segments --------key shmid owner perms bytes nattch status0x00000000 65536 root 600 524288 2 dest0x00000000 229377 root 600 524288 2 dest0x00000000 7143426 root 600 16777216 2 dest0x00802c81 400392195 mingjie.gm 600 56 60x00000000 491524 root 600 524288 2 dest0x00000000 212926469 mingjie.gm 640 2928640 1640x00000000 212959238 mingjie.gm 640 1677721600 820x00000000 884743 root 600 524288 2 dest0x00000000 212992008 mingjie.gm 640 13848576 820xb4ce0a78 213024777 mingjie.gm 640 12288 820x00803069 388169738 mingjie.gm 600 56 6#define _GNU_SOURCE------ Semaphore Arrays --------key semid owner perms nsems0x671cfa8c 6422528 mingjie.gm 640 1520x671cfa8d 6455297 mingjie.gm 640 1520x671cfa8e 6488066 mingjie.gm 640 152
执行程序
[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 --------key msqid owner perms used-bytes messages------ Shared Memory Segments --------key shmid owner perms bytes nattch status------ Semaphore Arrays --------key semid owner perms nsems
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有个毛用啊?我们知道,在传统的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 UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD4 S root 1 0 0 80 0 - 29185 do_wai 20:04 pts/3 00:00:00 /bin/bash0 R root 35 1 0 80 0 - 38833 - 20:05 pts/3 00: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_map 0 1000 1
再比如下面的示例:表示把内部从0开始的uid映射到外部从0开始的uid,其最大范围是无符号32位整形
$ cat /proc/$/uid_map 0 0 4294967295
User 是以普通用户运行,但是别的需要root权限,那么,如果我要同时使用多个,该怎么办呢?一般来说,我们先用一般用户创建User ,然后把这个一般用户映射成root,在容器内用root来创建其它的。
2.6
2.7 文件
同一个容器的进程在一个NS下。
本文到此结束,希望对大家有所帮助。
本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至81118366@qq.com举报,一经查实,本站将立刻删除。发布者:简知小编,转载请注明出处:https://www.jianzixun.com/95080.html