Linux Namespace系列(01):Namespace概述

Namespace是对全局系统资源的一种封装隔离,使得处于不一样namespace的进程拥有独立的全局系统资源,改变一个namespace中的系统资源只会影响当前namespace里的进程,对其余namespace中的进程没有影响。html

下面的全部例子都在ubuntu-server-x86_64 16.04下执行经过node

Linux内核支持的namespaces

目前,Linux内核里面实现了7种不一样类型的namespace。linux

名称        宏定义             隔离内容
Cgroup      CLONE_NEWCGROUP   Cgroup root directory (since Linux 4.6)
IPC         CLONE_NEWIPC      System V IPC, POSIX message queues (since Linux 2.6.19)
Network     CLONE_NEWNET      Network devices, stacks, ports, etc. (since Linux 2.6.24)
Mount       CLONE_NEWNS       Mount points (since Linux 2.4.19)
PID         CLONE_NEWPID      Process IDs (since Linux 2.6.24)
User        CLONE_NEWUSER     User and group IDs (started in Linux 2.6.23 and completed in Linux 3.8)
UTS         CLONE_NEWUTS      Hostname and NIS domain name (since Linux 2.6.19)

注意: 因为Cgroup namespace在4.6的内核中才实现,而且和cgroup v2关系密切,如今普及程度还不高,好比docker如今就尚未用它,因此在namespace这个系列中不会介绍Cgroup namespace。docker

查看进程所属的namespaces

系统中的每一个进程都有/proc/[pid]/ns/这样一个目录,里面包含了这个进程所属namespace的信息,里面每一个文件的描述符均可以用来做为setns函数(后面会介绍)的参数。ubuntu

#查看当前bash进程所属的namespace
dev@ubuntu:~$ ls -l /proc/$$/ns     
total 0
lrwxrwxrwx 1 dev dev 0 7月 7 17:24 cgroup -> cgroup:[4026531835] #(since Linux 4.6)
lrwxrwxrwx 1 dev dev 0 7月 7 17:24 ipc -> ipc:[4026531839]       #(since Linux 3.0)
lrwxrwxrwx 1 dev dev 0 7月 7 17:24 mnt -> mnt:[4026531840]       #(since Linux 3.8)
lrwxrwxrwx 1 dev dev 0 7月 7 17:24 net -> net:[4026531957]       #(since Linux 3.0)
lrwxrwxrwx 1 dev dev 0 7月 7 17:24 pid -> pid:[4026531836]       #(since Linux 3.8)
lrwxrwxrwx 1 dev dev 0 7月 7 17:24 user -> user:[4026531837]     #(since Linux 3.8)
lrwxrwxrwx 1 dev dev 0 7月 7 17:24 uts -> uts:[4026531838]       #(since Linux 3.0)
  • 上面每种类型的namespace都是在不一样的Linux版本被加入到/proc/[pid]/ns/目录里去的,好比pid namespace是在Linux 3.8才被加入到/proc/[pid]/ns/里面,但这并非说到3.8才支持pid namespace,其实pid namespace在2.6.24的时候就已经加入到内核了,在那个时候就能够用pid namespace了,只是有了/proc/[pid]/ns/pid以后,使得操做pid namespace更方便了segmentfault

  • 虽说cgroup是在Linux 4.6版本才被加入内核,但是在Ubuntu 16.04上,尽管内核版本才4.4,但也支持cgroup namespace,估计应该是Ubuntu将4.6的cgroup namespace这部分代码patch到了他们的4.4内核上。bash

  • 以ipc:[4026531839]为例,ipc是namespace的类型,4026531839是inode number,若是两个进程的ipc namespace的inode number同样,说明他们属于同一个namespace。这条规则对其余类型的namespace也一样适用。dom

  • 从上面的输出能够看出,对于每种类型的namespace,进程都会与一个namespace ID关联。socket

跟namespace相关的API

和namespace相关的函数只有三个,这里简单的看一下,后面介绍UTS namespace的时候会有详细的示例函数

clone: 建立一个新的进程并把他放到新的namespace中

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

flags: 
    指定一个或者多个上面的CLONE_NEW*(固然也能够包含跟namespace无关的flags), 
    这样就会建立一个或多个新的不一样类型的namespace, 
    并把新建立的子进程加入新建立的这些namespace中。

setns: 将当前进程加入到已有的namespace中

int setns(int fd, int nstype);

fd: 
    指向/proc/[pid]/ns/目录里相应namespace对应的文件,
    表示要加入哪一个namespace

nstype:
    指定namespace的类型(上面的任意一个CLONE_NEW*):
    1. 若是当前进程不能根据fd获得它的类型,如fd由其余进程建立,
    并经过UNIX domain socket传给当前进程,
    那么就须要经过nstype来指定fd指向的namespace的类型
    2. 若是进程能根据fd获得namespace类型,好比这个fd是由当前进程打开的,
    那么nstype设置为0便可

unshare: 使当前进程退出指定类型的namespace,并加入到新建立的namespace(至关于建立并加入新的namespace)

int unshare(int flags);

flags:
    指定一个或者多个上面的CLONE_NEW*,
    这样当前进程就退出了当前指定类型的namespace并加入到新建立的namespace

clone和unshare的区别

clone和unshare的功能都是建立并加入新的namespace, 他们的区别是:

  • unshare是使当前进程加入新的namespace

  • clone是建立一个新的子进程,而后让子进程加入新的namespace,而当前进程保持不变

其它

当一个namespace中的全部进程都退出时,该namespace将会被销毁。固然还有其余方法让namespace一直存在,假设咱们有一个进程号为1000的进程,以ipc namespace为例:

  1. 经过mount --bind命令。例如mount --bind /proc/1000/ns/ipc /other/file,就算属于这个ipc namespace的全部进程都退出了,只要/other/file还在,这个ipc namespace就一直存在,其余进程就能够利用/other/file,经过setns函数加入到这个namespace

  2. 在其余namespace的进程中打开/proc/1000/ns/ipc文件,并一直持有这个文件描述符不关闭,之后就能够用setns函数加入这个namespace。

参考