Linux Namespace 特性简要介绍
Contents
什么是 Namespace
Namespace 是 Linux 内核中实现的特性,本质上是一种资源隔离方案。
Namespace,顾名思义,为不同的进程集合提供不同的「命名空间」,不同进程集合彼此不能访问其对应的「命名空间」,而「命名空间」其实就是其资源集合。
Namespace 提供了一种抽象机制,将原本全局共享的资源隔离成不同的集合,集合中的成员独享其原本全局共享的资源。
举个例子:进程 A 和进程 B 分别属于两个不同的 Namespace,那么进程 A 将可以使用 Linux 内核提供的所有 Namespace 资源:如独立的主机名,独立的文件系统,独立的进程编号等等。同样地,进程 B 也可以使用同类资源,但其资源与进程 A 使用的资源相互隔离,彼此无法感知。从用户层面来看,进程 A 读写属于 A 的 Namespace 资源,进程 B 读写属于 B 的 Namespace 资源,彼此之间安全隔离。
Docker 就是利用 Namespace 这个特性,实现了容器之间的资源隔离。本质上来看,每一个 Docker 容器就是宿主机进程,不同 Docker 容器就对应不同的宿主机进程,这样,不同容器(即不同进程)就可以采用 Namespace 资源隔离,使得每一个容器看起来都像是一个独立的小虚拟机。
Linux 内核提供了 7 种不同的 Namespace,如下所示:
Namespace | clone() 使用的 flag |
所隔离的资源 |
---|---|---|
Cgroup | CLONE_NEWCGROUP |
Cgroup 根目录 |
IPC | CLONE_NEWIPC |
System V IPC,POSIX 消息队列 |
Network | CLONE_NEWNET |
网络设备、协议栈、端口等 |
Mount | CLONE_NEWNS |
挂载点 |
PID | CLONE_NEWPID |
进程 ID |
User | CLONE_NEWUSER |
用户和组 ID |
UTS | CLONE_NEWUTS |
主机名和域名 |
怎么使用 Namespace
3 个系统调用
Namespace 乍一看似乎是非常复杂的特性,但其使用方式非常简单,只需要 3 个 API 即可,这 3 个 API 分别对应 3 个系统调用:
clone()
创建一个新的进程,通过不同的 flag(如上表格所示)将同时创建不同资源类型的 namespace,并将该进程作为其 namespace 的一个成员。
clone()
的原型为:
|
|
其中:
child_fn
:子进程运行的程序子函数;child_stack
:子进程所使用的栈空间;flags
:使用哪些CLONE_*
的标志量,不同的 flag 可以使用布尔或的方式进行叠加。该变量的低位的几个 bytes 代表着当子进程终结之后,将给父进程发送什么样的信号。如果是SIGCHLD
,则当子进程结束后,会向父进程发送SIGCHLD
信号,父进程此时可用waitpid()
来等待子进程结束。如果不指定信号,则子进程结束后将不向父进程发送任何信号;arg
:可传入的用户参数;
函数执行成功将返回子进程的 PID,否则(发生错误)返回 -1。
setns()
让调用进程加入某个已存在的 namespace。
setns()
的原型是:
|
|
其中:
-
fd
:要加入的 namespace 的文件描述符,一般为/proc/[pid]/ns
下某个对应类型 namespace 的软链接; -
nstype
:调用进程想要加入的 namesapce 的类型,其类型对应上文的表格中的 7 种 Namespace 类型:0
:允许加入任何类型的 namespace;CLONE_NEWCGROUP
:fd
必须指向一个 cgroup 的 namespace;CLONE_NEWIPC
:fd
必须指向一个 IPC 的 namespace;CLONE_NEWNET
:fd
必须指向一个 network 的 namespace;CLONE_NEWNS
:fd
必须指向一个 mount 的 namespace;CLONE_NEWPID
:fd
必须指向一个 pid 的 namespace;CLONE_NEWUSER
:fd
必须指向一个 user 的 namespace;CLONE_NEWUTS
:fd
必须指向一个 UTS 的 namespace;
unshare()
让调用进程移入一个新的 namespace。这个调用用的不是很多,有空再过来补充一下。
备注:使用 clone()
和 unshare()
在大多数情况下都需要 CAP_SYS_ADMIN
权限(即 root 权限)。
/proc
目录与 Namespace
除了 3 个 API 之外,还需要关注 /proc
目录。
自 Linux 3.8 开始,用户就可以在 /proc/[pid]/ns
目录下看到指向不同 namespace 号的文件,如:
|
|
每一个进程在其对应的 /proc/[pid]/ns
下都有其 namespace 信息,该目录下每一个文件都是一个软链接,setns()
可以通过打开对应进程下的 ns 目录下的软链接文件来加入到对应的 namespace 中。
该目录下的每个软链接的内容其实是一串字符,由 namespace 类型和 inode number 组成:
|
|
且满足以下几个条件:
-
若几个进程中对应 namespace 软链接内容一致,则这几个进程同属于同一个 namespace;
-
即使 namespace 中的进程全部终结了,只要其软链接文件一直处于 open 状态,则 namespace 将一直存在;