Namespace是哪些?
linux的namespace机制有点类似于数据库中的schema,可以为不同的进程提供各自的命名空间,命名空间相互隔离,进程跑在自己的namespace中资源相互隔离。
嵌入式进阶教程分门别类整理好了,看的时侯非常便捷,因为内容较多,这儿就截取一部份图吧。
须要的同事私信【内核】即可申领。
内核学习地址:Linux内核源码/显存调优/文件系统/进程管理/设备驱动/网路合同栈-学习视频教程-腾讯课堂
docker使用了namespace的机制,将进程隔离在某个namespace中,而在某一个命名空间内的进程可以感知到其他进程的存在,并且对空间外部的进程一无所知。以一种轻量级的方法实现了虚拟化技术。
namespace提供了多种资源的隔离:
Namespace
clone(...flag...)
所隔离的资源
Cgroup
CLONE_NEWCGROUP
Cgroup根目录
IPC
CLONE_NEWIPC
SystemVIPC,POSIX消息队列
Network
CLONE_NEWNET
网路设备、协议栈、端口等
Mount
CLONE_NEWNS
挂载点
PID
CLONE_NEWPID
进程ID
User
CLONE_NEWUSER
用户和组ID
UTS
CLONE_NEWUTS
主机名和域名
多种类型的资源隔离可以让我们从文件系统开始,到进程通讯、网络通讯、用户权限管理、主机管理,一步步地实现各方面资源的隔离linux内核24版源代码分析大全(清晰版),使进程运行在一个虚拟化的环境中。本文讨论的namespace实现针对Linux内核3.8及其之后的版本。
下边我们针对六种命名空间的API做一些实例讲解,亲身体验隔离的实现底层机制。
2Namespace实战
关于namespace的系统调用主要有三个:
结合一段代码来介绍几个namespace
#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.1UTSNamespace
#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;
}
hostname早已变了
[root@iZbp1d4tisi44j6vxze02fZ tmp]# vi c1.c
[root@iZbp1d4tisi44j6vxze02fZ tmp]# gcc c1.c
[root@iZbp1d4tisi44j6vxze02fZ tmp]# ./a.out
Parent - start a container!
Container - inside the container!
[root@container tmp]# hostname
container
[root@container tmp]# uname -n
container
[root@container tmp]# exit
exit
Parent - container stopped!
[root@iZbp1d4tisi44j6vxze02fZ tmp]#
2.2UTSNamespace
IPC全称Inter-ProcessCommunication,是Unix/Linux下进程间通讯的一种形式,IPC有共享显存、信号量、消息队列等方式。所以,为了隔离,我们也须要把IPC给隔离开来,这样,只有在同一个Namespace下的进程能够相互通信。
假如你熟悉IPC的原理的话,你会晓得linux系统,IPC须要有一个全局的ID,即然是全局的,这么就意味着我们的Namespace须要对这个ID隔离,不能让别的Namespace的进程听到。
#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 -Q
Message queue id: 0
[root@iZbp1d4tisi44j6vxze02fZ tmp]# ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0x3b786aa7 0 root 644 0 0
查看所有ipcs
[root@iZbp1d4tisi44j6vxze02fZ tmp]# ipcs -a
------ Message Queues --------
key msqid owner perms used-bytes messages
0x3b786aa7 0 root 644 0 0
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 65536 root 600 524288 2 dest
0x00000000 229377 root 600 524288 2 dest
0x00000000 7143426 root 600 16777216 2 dest
0x00802c81 400392195 mingjie.gm 600 56 6
0x00000000 491524 root 600 524288 2 dest
0x00000000 212926469 mingjie.gm 640 2928640 164
0x00000000 212959238 mingjie.gm 640 1677721600 82
0x00000000 884743 root 600 524288 2 dest
0x00000000 212992008 mingjie.gm 640 13848576 82
0xb4ce0a78 213024777 mingjie.gm 640 12288 82
0x00803069 388169738 mingjie.gm 600 56 6
#define _GNU_SOURCE
------ Semaphore Arrays --------
key semid owner perms nsems
0x671cfa8c 6422528 mingjie.gm 640 152
0x671cfa8d 6455297 mingjie.gm 640 152
0x671cfa8e 6488066 mingjie.gm 640 152
执行程序
[root@iZbp1d4tisi44j6vxze02fZ tmp]# gcc c2.c -o ipc
[root@iZbp1d4tisi44j6vxze02fZ tmp]# ./ipc
Parent - 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.3PIDNamespace
#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]# ./pid
Parent - start a container!
Container [ 1] - inside the container!
[root@container tmp]# echo $
你可能会问,PID为1有个毛用啊?我们晓得,在传统的UNIX系统中,PID为1的进程是init,地位十分特殊。他作为所有进程的父进程,有好多特权(例如:屏蔽讯号等)linux内核24版源代码分析大全(清晰版),另外,其都会检测所有进程的状态,我们晓得linux命令ls,假如某个子进程脱离了父进程(父进程没有wait它),这么init都会负责回收资源并结束这个子进程。所以,要做到进程空间的隔离,首先要创建出PID为1的进程,最好如同chroot那样,把子进程的PID在容器内弄成1。
然而,我们会发觉,在子进程的shell里输入ps,top等命令,我们还是可以看得到所有进程。说明并没有完全隔离。这是由于,像ps,top这种命令会去读/proc文件系统,所以,由于/proc文件系统在父进程和子进程都是一样的,所以这种命令显示的东西都是一样的。
所以,我们还须要对文件系统进行隔离。
2.4MountNamespace
下边的类库中,我们在启用了mountnamespace并在子进程中重新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.out
Parent [16901] - start a container!
Container [ 1] - inside the container!
[root@container tmp]# ps -elf
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
4 S root 1 0 0 80 0 - 29185 do_wai 20:04 pts/3 00:00:00 /bin/bash
0 R root 35 1 0 80 0 - 38833 - 20:05 pts/3 00:00:00 ps -elf
在通过CLONE_NEWNS创建mountnamespace后,父进程会把自己的文件结构复制给子进程中。而子进程中新的namespace中的所有mount操作都只影响自身的文件系统,而不对外界形成任何影响。这样可以做到比较严格地隔离。
里面的代码是在子进程中重新mount了一次,否则还是会听到父进程的proc目录!
2.5UserNamespace
UserNamespace主要是用了CLONE_NEWUSER的参数。使用了这个参数后,内部见到的UID和GID早已与外部不同了,默认显示为65534。那是由于容器找不到其真正的UID所以,设置上了最大的UID(其设置定义在/proc/sys/kernel/overflowuid)。
要把容器中的uid和真实系统的uid给映射在一起,须要更改/proc//uid_map和/proc//gid_map这两个文件。这两个文件的格式为:ID-inside-nsID-outside-nslength
其中:
$ cat /proc/2465/uid_map
0 1000 1
再例如下边的示例:表示把namespace内部从0开始的uid映射到外部从0开始的uid,其最大范围是无符号32位整形
$ cat /proc/$/uid_map
0 0 4294967295
UserNamespace是以普通用户运行,并且别的Namespace须要root权限,这么,假如我要同时使用多个Namespace,该如何办呢?通常来说,我们先用通常用户创建UserNamespace,之后把这个通常用户映射成root,在容器内用root来创建其它的Namesapce。
2.6NetworkNamespace
2.7Namespace文件
同一个容器的进程在一个NS下。