乐趣区

关于程序员:Docker核心技术Namespace浅析

简介

Linux `Namespace` 提供了一种内核级别隔离系统资源的办法,通过将零碎的全局资源放在不同的 `Namespace` 中,来实现资源隔离的目标。不同 `Namespace` 的程序,能够享有一份独立的系统资源。目前 Linux 中提供了六类系统资源的隔离机制,别离是:
  • Mount: 隔离文件系统挂载点
  • UTS: 隔离主机名和域名信息
  • IPC: 隔离过程间通信
  • PID: 隔离过程的 ID
  • Network: 隔离网络资源
  • User: 隔离用户和用户组的 ID

上面简略的介绍一下这些 Namespace 的应用和性能。

应用

波及到 `Namespace` 的操作接口包含 `clone()`、`setns()`、`unshare()` 以及还有 `/proc` 下的局部文件。为了应用特定的 `Namespace`,在应用这些接口的时候须要指定以下一个或多个参数:
  • CLONE_NEWNS: 用于指定Mount Namespace
  • CLONE_NEWUTS: 用于指定UTS Namespace
  • CLONE_NEWIPC: 用于指定IPC Namespace
  • CLONE_NEWPID: 用于指定PID Namespace
  • CLONE_NEWNET: 用于指定Network Namespace
  • CLONE_NEWUSER: 用于指定User Namespace

上面简略概述一下这几个接口的用法。

clone 零碎调用

能够通过 clone 零碎调用来创立一个独立 Namespace 的过程,它的函数形容如下:

int clone(int (*child_func)(void *), void *child_stack, int flags, void *arg);

它通过 flags 参数来管制创立过程时的个性,比方新创建的过程是否与父过程共享虚拟内存等。比方能够传入 CLONE_NEWNS 标记使得新创建的过程领有独立的Mount Namespace,也能够传入多个 flags 使得新创建的过程领有多种个性,比方:

flags = CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC;

传入这个 flags 那么新创建的过程将同时领有独立的 Mount NamespaceUTS NamespaceIPC Namespace

通过 /proc 文件查看已存在的 Namespace

在 3.8 内核开始,用户能够在 /proc/$pid/ns 文件下看到本过程所属的 Namespace 的文件信息。例如 PID 为 2704 过程的状况如下图所示:

其中 4026531839 是 Namespace 的 ID,如果两个过程的 Namespace ID 雷同表明过程同处于一个命名空间中。
这里须要留神的是 :只/proc/$pid/ns/ 对应的 Namespace 文件被关上,并且该文件描述符存在,即便该 PID 所属的过程被销毁,这个 Namespace 会仍然存在。能够通过挂载的形式关上文件描述符:

touch ~/mnt
mount --bind /proc/2704/mnt ~/mnt

这样就能够保留住 PID 为 2704 的过程的 Mount Namespace 了,即便 2704 过程被销毁或者退出,ID 为 4026531840 的 Mount Namespace 仍然会存在。

setns 退出已存在的 Namepspace

setns()函数能够把过程退出到指定的 Namespace 中,它的函数形容如下:

int setns(int fd, int nstype);

它的参数形容如下:

  • fd参数:示意文件描述符,后面提到能够通过关上 /proc/$pid/ns/ 的形式将指定的 Namespace 保留下来,也就是说能够通过文件描述符的形式来索引到某个Namespace
  • nstype参数:用来查看 fd 关联 Namespace 是否与 nstype 表明的 Namespace 统一,如果填 0 的话示意不进行该项查看。

通过在程序中调用 setns 来将过程退出到指定的 Namespace 中。

unshare 脱离到新的 Namespace

unshare()零碎调用用于将以后过程和所在的 Namespace 拆散,并退出到一个新的 Namespace 中,绝对于 setns() 零碎调用来说,unshare()不必关联之前存在的 Namespace,只须要指定须要拆散的Namespace 就行,该调用会主动创立一个新的 Namespace
unshare() 的函数形容如下:

int unshare(int flags);

其中 flags 用于指明要拆散的资源类别,它反对的 flagsclone零碎调用反对的 flags 相似,这里简要的叙述一下几种标记:

  • CLONE_FILES: 子过程个别会共享父过程的文件描述符,如果子过程不想共享父过程的文件描述符了,能够通过这个 flag 来勾销共享。
  • CLONE_FS: 使以后过程不再与其余过程共享文件系统信息。
  • CLONE_SYSVSEM: 勾销与其余过程共享 SYS V 信号量。
  • CLONE_NEWIPC: 创立新的IPC Namespace,并将该过程退出进来。

注意事项

这里须要留神的是 unshare()setns()零碎调用对 PID Namespace 的解决不太雷同,当 unshare PID namespace 时,调用过程会为它的子过程调配一个新的 PID Namespace,然而调用过程自身不会被移到新的Namespace 中。而且调用过程第一个创立的子过程在新 Namespace 中的 PID 为 1,并成为新 Namespace 中的 init 过程。
setns()零碎调用也是相似的,调用者过程并不会进入新的 PID Namespace,而是随后创立的子过程会进入。
为什么创立其余的 Namespace 时 unshare()setns()会间接进入新的 Namespace,而唯独 PID Namespace 不是如此呢?
因为调用 getpid() 函数失去的 PID 是依据调用者所在的 PID Namespace 而决定返回哪个 PID,进入新的 PID namespace 会导致 PID 产生变动。而对用户态的程序和库函数来说,他们都认为过程的 PID 是一个常量,PID 的变动会引起这些过程奔溃。
换句话说,一旦程序过程创立当前,那么它的 PID namespace 的关系就确定下来了,过程不会变更他们对应的 PID namespace。

小结

通过下面简略的概述,对于 Namespace 的操作有以下形式:

  • 1、能够在过程刚创立的时候通过 clone 零碎调用为新过程调配一个或多个新的Namespace
  • 2、通过 setns() 将过程退出到已有的 Namespace 中。
  • 3、通过 unshare() 为已存在的过程创立一个或多个新的Namespace

接下来具体的介绍一下各个 Namespace 的性能和个性。

Mount Namespace

Mount Namespace用来隔离文件系统的挂载点,不同 Mount Namespace 的过程领有不同的挂载点,同时也领有了不同的文件系统视图。Mount Namespace是历史上第一个反对的 Namespace,它通过CLONE_NEWNS 来标识的。

挂载的概念

挂载的过程是通过 mount 零碎调用实现的,它有两个参数:一个是已存在的一般文件名,一个是能够间接拜访的非凡文件,一个是非凡文件的名字。这个非凡文件个别用来关联一些存储卷,这个存储卷能够蕴含本人的目录层级和文件系统构造。
mount所达到的成果是:像拜访一个一般的文件一样拜访位于其余设施上文件系统的根目录,也就是将该设施上目录的根节点挂到了另外一个文件系统的页节点上,达到给这个文件系统裁减容量的目标。
能够通过 /proc 文件系统查看一个过程的挂载信息,具体做法如下:

cat /proc/$pid/mountinfo

其输入后果如下:

其中输入的格局如下:

挂载流传

过程在创立 Mount Namespace 时,会把以后的文件构造复制给新的 Namespace,新的Namespace 中的所有 mount 操作仅影响本身的文件系统。但随着引入 挂载流传 的个性,Mount Namespace变得并不是齐全意义上的资源隔离,这种流传个性使得多 Mount Namespace 之间的挂载事件能够相互影响。
挂载流传定义了挂载对象之间的关系,零碎利用这些关系来决定挂载对象中的挂载事件对其余挂载对象的影响。其中挂载对象之间的关系形容如下:

  • 共享关系 (MS_SHARED):一个挂载对象的挂载事件会跨Namespace 共享到其余挂载对象。
  • 从属关系(MS_SLAVE): 流传的方向是单向的,即只能从 Master 流传到 Slave 方向。
  • 公有关系 (MS_PRIVATE): 不同Namespace 的挂载事件是互不影响的(默认选项)。
  • 不可绑定关系(MS_UNBINDABLE): 一个不可绑定的公有挂载,与公有挂载相似,然而不能执行挂载操作。

其中给挂载点设置挂载关系的例子如下:

mount --make-shared /mntS      # 将挂载点设置为共享关系属性
 mount --make-private /mntP     # 将挂载点设置为公有关系属性
 mount --make-slave /mntY       # 将挂载点设置为从属关系属性
 mount --make-unbindable /mntU  # 将挂载点设置为不可绑定属性

留神在设置公有关系属性时,在本命名空间下的这个挂载点是 Slave,而父命名空间下这个挂载点是 Master,挂载流传的方向只能由 Master 传给 Slave。

绑定挂载

绑定挂载的引入使得 mount 的其中一个参数不肯定要是一个非凡文件,也能够是该文件系统上的一个一般文件目录。Linux 中绑定挂载的用法如下:

mount --bind /home/work /home/alpha
mount -o bind /home/work /home/alpha

其中 /home/work 是磁盘上的存在的一个目录,而不是一个文件设施 (比方磁盘分区)。如果须要将 Linux 中两个文件目录链接起来,能够通过绑定挂载的形式,挂载后的成果相似于在两个文件目录上建设了硬链接。
在绑定挂载中同时会波及到挂载的流传个性,挂载流传的个性参考:Linux 绑定挂载

UTS Namespace

UTS Namespace提供了主机名和域名的隔离,也就是 struct utsname 里的 nodenamedomainname两个字段。不同 Namespace 中能够领有独立的主机名和域名。
那么为什么须要对主机名和域名进行隔离呢?因为主机名和域名能够用来代替 IP 地址,如果没有这一层隔离,同一主机上不同的容器的网络拜访就可能出问题。

IPC Namespace

IPC Namespace是对过程间通信的隔离,过程间通信常见的办法有信号量、音讯队列和共享内存。IPC Namespace次要针对的是 SystemV IPC 和 Posix 音讯队列,这些 IPC 机制都会用到标识符,比方用标识符来辨别不同的音讯队列,IPC Namespace要达到的指标是雷同的标识符在不同的 Namepspace 中代表不同的通信介质 (比方信号量、音讯队列和共享内存)。
原文

更多深度文章, 关注: 二进制社区

退出移动版