操作系統通過虛擬內存技術,使得每個用戶進程都認為自己擁有所有的物理內存,這是操作系統對內存的虛擬化。操作系統通過分時調度系統,每個進程都能被【公平地】調度執行,即每個進程都能獲取到CPU,使得每個進程都認為自己在進程活動期間擁有所有的CPU時間,這是操作系統對CPU的虛擬化。
從這兩種虛擬化方式可推知,當使用某種虛擬化技術去管理進程時,進程會認為自己擁有某種物理資源的全部。
虛擬內存和分時系統均是對「物理資源」進行虛擬化,其實操作系統中還有很多「非物理資源」,比如用戶權限系統資源、網絡協議棧資源、文件系統掛載路徑資源等。通過Linux的namespace功能,可以對這些非物理全局資源進行虛擬化。
Linux namespace是在當前運行的系統環境中創建(隔離)另一個進程的運行環境出來,并在此運行環境中將一些必要的系統全局資源進行【虛擬化】。進程可以運行在指定的namespace中,因此,namespace中的每個進程都認為自己擁有所有這些虛擬化的全局資源。
目前,Linux已經支持8種全局資源的虛擬化(每種資源都是隨著Linux內核版本的迭代而逐漸加入的,因此有些內核版本可能不具備某種namespace):
-
cgroup namespace:該namespace可單獨管理自己的cgroup
-
ipc namespace:該namespace有自己的IPC,比如共享內存、信號量等
-
network namespace:該namespace有自己的網絡資源,包括網絡協議棧、網絡設備、路由表、防火墻、端口等
-
mount namespace:該namespace有自己的掛載信息,即擁有獨立的目錄層次
-
pid namespace:該namespace有自己的進程號,使得namespace中的進程PID單獨編號,比如可以PID=1
-
time namespace:該namespace有自己的啟動時間點信息和單調時間,比如可設置某個namespace的開機時間點為1年前啟動,再比如不同的namespace創建后可能流逝的時間不一樣
-
user namespace:該namespace有自己的用戶權限管理機制(比如獨立的UID/GID),使得namespace更安全
-
uts namespace:該namepsace有自己的主機信息,包括主機名(hostname)、NIS domain name
用戶可以同時創建具有多種資源類型的namespace,比如創建一個同時具有uts、pid和user的namespace。
理解Linux namespace
用戶可以創建指定類型的namespace并將程序放入該namespace中運行,這表示從當前的系統運行環境中隔離一個進程的運行環境,在此namespace中運行的進程將認為自己享有該namespace中的獨立資源。
實際上,即使用戶沒有手動創建Linux namespace,Linux系統開機后也會創建一個默認的namespace,稱為「root namespace」,所有進程默認都運行在root namespace中,每個進程都認為自己擁有該namespace中的所有系統全局資源。
回顧一下Linux的開機啟動流程,內核加載成功后將初始化系統運行環境,這個運行環境就是root namespace環境,系統運行環境初始化完成后,便可以認為操作系統已經開始工作了。
「每一個namespace都基于當前內核」,無論是默認的root namespace還是用戶創建的每一個namespace,都基于當前內核工作。所以可以認為namespace是內核加載后啟動的一個特殊系統環境,用戶進程可以在此環境中獨立享用資源。更嚴格地說,「root namespace直接基于內核,而用戶創建的namespace運行環境基于當前所在的namespace」。之所以用戶創建的namespace不直接基于內核環境,是因為每一個namespace可能都會修改某些運行時內核參數。
比如,用戶創建的uts namespace1中修改了主機名為ns1,然后在namespace1中創建uts namespace2時,namespace2默認「將共享namespace1的其他資源并拷貝namespace1的主機名資源」,因此namespace2的主機名初始時也是ns1。當然,namespace2是隔離的,可以修改其主機名為ns2,這不會影響其他namespace,修改后,將只有namespace2中的進程能看到其主機名為ns2。
可以通過如下方式查看某個進程運行在哪一個namespace中,即該進程享有的獨立資源來自于哪一個namespace。
#?ls?-l?/proc/<PID>/ns
$?ls?-l?/proc/$$/ns?|?awk?'{print?$1,$(NF-2),$(NF-1),$NF}'
lrwxrwxrwx??cgroup????????????->??cgroup:[4026531835]
lrwxrwxrwx??ipc???????????????->??ipc:[4026531839]
lrwxrwxrwx??mnt???????????????->??mnt:[4026531840]
lrwxrwxrwx??net???????????????->??net:[4026531992]
lrwxrwxrwx??pid???????????????->??pid:[4026531836]
lrwxrwxrwx??pid_for_children??->??pid:[4026531836]
lrwxrwxrwx??user??????????????->??user:[4026531837]
lrwxrwxrwx??uts???????????????->??uts:[4026531838]$?sudo?ls?-l?/proc/1/ns?|?awk?'{print?$1,$(NF-2),$(NF-1),$NF}'
lrwxrwxrwx??cgroup????????????->??cgroup:[4026531835]
lrwxrwxrwx??ipc???????????????->??ipc:[4026531839]
lrwxrwxrwx??mnt???????????????->??mnt:[4026531840]
lrwxrwxrwx??net???????????????->??net:[4026531992]
lrwxrwxrwx??pid???????????????->??pid:[4026531836]
lrwxrwxrwx??pid_for_children??->??pid:[4026531836]
lrwxrwxrwx??user??????????????->??user:[4026531837]
lrwxrwxrwx??uts???????????????->??uts:[4026531838]
這些文件表示當前進程打開的namespace資源,每一個文件都是一個軟鏈接,所指向的文件是一串格式特殊的名稱。冒號后面中括號內的數值表示該namespace的inode。如果不同進程的namespace inode相同,說明這些進程屬于同一個namespace。
從結果上來看,每個進程都運行在多個namespace中,且pid=1和pid=$$(當前Shell進程)兩個進程的namespace完全一樣,說明它們運行在相同的環境下(root namespace)