Namespace是對全局系統資源的一種封裝隔離,使得處於不一樣namespace的進程擁有獨立的全局系統資源,改變一個namespace中的系統資源只會影響當前namespace裏的進程,對其餘namespace中的進程沒有影響。html
下面的全部例子都在ubuntu-server-x86_64 16.04下執行經過node
目前,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
系統中的每一個進程都有/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相關的函數只有三個,這裏簡單的看一下,後面介紹UTS namespace的時候會有詳細的示例函數
int clone(int (*child_func)(void *), void *child_stack , int flags, void *arg); flags: 指定一個或者多個上面的CLONE_NEW*(固然也能夠包含跟namespace無關的flags), 這樣就會建立一個或多個新的不一樣類型的namespace, 並把新建立的子進程加入新建立的這些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便可
int unshare(int flags); flags: 指定一個或者多個上面的CLONE_NEW*, 這樣當前進程就退出了當前指定類型的namespace並加入到新建立的namespace
clone和unshare的功能都是建立並加入新的namespace, 他們的區別是:
unshare是使當前進程加入新的namespace
clone是建立一個新的子進程,而後讓子進程加入新的namespace,而當前進程保持不變
當一個namespace中的全部進程都退出時,該namespace將會被銷燬。固然還有其餘方法讓namespace一直存在,假設咱們有一個進程號爲1000的進程,以ipc namespace爲例:
經過mount --bind命令。例如mount --bind /proc/1000/ns/ipc /other/file,就算屬於這個ipc namespace的全部進程都退出了,只要/other/file還在,這個ipc namespace就一直存在,其餘進程就能夠利用/other/file,經過setns函數加入到這個namespace
在其餘namespace的進程中打開/proc/1000/ns/ipc文件,並一直持有這個文件描述符不關閉,之後就能夠用setns函數加入這個namespace。