您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關Linux操作系統中Namespace的作用是什么,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
namespace的概念
namespace 是 Linux 內核用來隔離內核資源的方式。通過 namespace 可以讓一些進程只能看到與自己相關的一部分資源,而另外一些進程也只能看到與它們自己相關的資源,這兩撥進程根本就感覺不到對方的存在。具體的實現方式是把一個或多個進程的相關資源指定在同一個 namespace 中。
Linux namespaces 是對全局系統資源的一種封裝隔離,使得處于不同 namespace 的進程擁有獨立的全局系統資源,改變一個 namespace 中的系統資源只會影響當前 namespace 里的進程,對其他 namespace 中的進程沒有影響。
namespace的用途
可能絕大多數的使用者和我一樣,是在使用 docker 后才開始了解 linux 的 namespace 技術的。實際上,Linux 內核實現 namespace 的一個主要目的就是實現輕量級虛擬化(容器)服務。在同一個 namespace 下的進程可以感知彼此的變化,而對外界的進程一無所知。這樣就可以讓容器中的進程產生錯覺,認為自己置身于一個獨立的系統中,從而達到隔離的目的。也就是說 linux 內核提供的 namespace 技術為 docker 等容器技術的出現和發展提供了基礎條件。
我們可以從 docker 實現者的角度考慮該如何實現一個資源隔離的容器。比如是不是可以通過 chroot 命令切換根目錄的掛載點,從而隔離文件系統。為了在分布式的環境下進行通信和定位,容器必須要有獨立的 IP、端口和路由等,這就需要對網絡進行隔離。同時容器還需要一個獨立的主機名以便在網絡中標識自己。接下來還需要進程間的通信、用戶權限等的隔離。運行在容器中的應用需要有進程號(PID),自然也需要與宿主機中的 PID 進行隔離。也就是說這六種隔離能力是實現一個容器的基礎,讓我們看看 linux 內核的 namespace 特性為我們提供了什么樣的隔離能力:
上表中的前六種 namespace 正是實現容器必須的隔離技術,至于新近提供的 Cgroup namespace 目前還沒有被 docker 采用。相信在不久的將來各種容器也會添加對 Cgroup namespace 的支持。
namespace的發展歷史
Linux 在很早的版本中就實現了部分的 namespace,比如內核 2.4 就實現了 mount namespace。大多數的 namespace 支持是在內核 2.6 中完成的,比如 IPC、Network、PID、和 UTS。還有個別的 namespace 比較特殊,比如 User,從內核 2.6 就開始實現了,但在內核 3.8 中才宣布完成。同時,隨著 Linux 自身的發展以及容器技術持續發展帶來的需求,也會有新的 namespace 被支持,比如在內核 4.6 中就添加了 Cgroup namespace。
Linux 提供了多個 API 用來操作 namespace,它們是 clone()、setns() 和 unshare() 函數,為了確定隔離的到底是哪項 namespace,在使用這些 API 時,通常需要指定一些調用參數:CLONE_NEWIPC、CLONE_NEWNET、CLONE_NEWNS、CLONE_NEWPID、CLONE_NEWUSER、CLONE_NEWUTS 和 CLONE_NEWCGROUP。如果要同時隔離多個 namespace,可以使用 | (按位或)組合這些參數。同時我們還可以通過 /proc 下面的一些文件來操作 namespace。下面就讓讓我們看看這些接口的簡要用法。
查看進程所屬的namespace
從版本號為 3.8 的內核開始,/proc/[pid]/ns 目錄下會包含進程所屬的 namespace 信息,使用下面的命令可以查看當前進程所屬的 namespace 信息:
$ ll /proc/$$/ns
首先,這些 namespace 文件都是鏈接文件。鏈接文件的內容的格式為 xxx:[inode number]。其中的 xxx 為 namespace 的類型,inode number 則用來標識一個 namespace,我們也可以把它理解為 namespace 的 ID。如果兩個進程的某個 namespace 文件指向同一個鏈接文件,說明其相關資源在同一個 namespace 中。
其次,在 /proc/[pid]/ns 里放置這些鏈接文件的另外一個作用是,一旦這些鏈接文件被打開,只要打開的文件描述符(fd)存在,那么就算該 namespace 下的所有進程都已結束,這個 namespace 也會一直存在,后續的進程還可以再加入進來。
除了打開文件的方式,我們還可以通過文件掛載的方式阻止 namespace 被刪除。比如我們可以把當前進程中的 uts 掛載到 ~/uts 文件:
$ touch ~/uts $ sudo mount --bind /proc/$$/ns/uts ~/uts
使用 stat 命令檢查下結果:
很神奇吧,~/uts 的 inode 和鏈接文件中的 inode number 是一樣的,它們是同一個文件。
clone函數
我們可以通過 clone() 在創建新進程的同時創建 namespace。clone() 在 C 語言庫中的聲明如下:
/* Prototype for the glibc wrapper function */ #define _GNU_SOURCE #include <sched.h> int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);
實際上,clone() 是在 C 語言庫中定義的一個封裝(wrapper)函數,它負責建立新進程的堆棧并且調用對編程者隱藏的 clone() 系統調用。Clone() 其實是 linux 系統調用 fork() 的一種更通用的實現方式,它可以通過 flags 來控制使用多少功能。一共有 20 多種 CLONE_ 開頭的 falg(標志位) 參數用來控制 clone 進程的方方面面(比如是否與父進程共享虛擬內存等),下面我們只介紹與 namespace 相關的 4 個參數:
fn:指定一個由新進程執行的函數。當這個函數返回時,子進程終止。該函數返回一個整數,表示子進程的退出代碼。
child_stack:傳入子進程使用的棧空間,也就是把用戶態堆棧指針賦給子進程的 esp 寄存器。調用進程(指調用 clone() 的進程)應該總是為子進程分配新的堆棧。
flags:表示使用哪些 CLONE_ 開頭的標志位,與 namespace 相關的有CLONE_NEWIPC、CLONE_NEWNET、CLONE_NEWNS、CLONE_NEWPID、CLONE_NEWUSER、CLONE_NEWUTS 和 CLONE_NEWCGROUP。
arg:指向傳遞給 fn() 函數的參數。
在后續的文章中,我們主要通過 clone() 函數來創建并演示各種類型的 namespace。
setns函數
通過 setns() 函數可以將當前進程加入到已有的 namespace 中。setns() 在 C 語言庫中的聲明如下:
#define _GNU_SOURCE #include <sched.h> int setns(int fd, int nstype);
和 clone() 函數一樣,C 語言庫中的 setns() 函數也是對 setns() 系統調用的封裝:
fd:表示要加入 namespace 的文件描述符。它是一個指向 /proc/[pid]/ns 目錄中文件的文件描述符,可以通過直接打開該目錄下的鏈接文件或者打開一個掛載了該目錄下鏈接文件的文件得到。
nstype:參數 nstype 讓調用者可以檢查 fd 指向的 namespace 類型是否符合實際要求。若把該參數設置為 0 表示不檢查。
前面我們提到:可以通過掛載的方式把 namespace 保留下來。保留 namespace 的目的是為以后把進程加入這個 namespace 做準備。在 docker 中,使用 docker exec 命令在已經運行著的容器中執行新的命令就需要用到 setns() 函數。為了把新加入的 namespace 利用起來,還需要引入 execve() 系列的函數(筆者在 《Linux 創建子進程執行任務》一文中介紹過 execve() 系列的函數,有興趣的同學可以前往了解),該函數可以執行用戶的命令,比較常見的用法是調用 /bin/bash 并接受參數運行起一個 shell。
unshare函數和命令
通過 unshare 函數可以在原進程上進行 namespace 隔離。也就是創建并加入新的 namespace 。unshare() 在 C 語言庫中的聲明如下:
#define _GNU_SOURCE #include <sched.h> int unshare(int flags);
和前面兩個函數一樣,C 語言庫中的 unshare() 函數也是對 unshare() 系統調用的封裝。調用 unshare() 的主要作用就是:不啟動新的進程就可以起到資源隔離的效果,相當于跳出原先的 namespace 進行操作。
系統還默認提供了一個叫 unshare 的命令,其實就是在調用 unshare() 系統調用。下面的 demo 使用 unshare 命令把當前進程的 user namespace 設置成了 root:
上述就是小編為大家分享的Linux操作系統中Namespace的作用是什么了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。