Linux Namespaces in operation記錄 - part 4


:本文絕大部分內容來自LinuxNamespaces實踐part4 ,原文系列文章詳細描述了Linux Namespace相關內容,英語過關的建議閱讀原文,本文內容主要用來記錄學習內容,如有不當之處還請評論區指正。

PID Namespace

一個新創建的PID Namespace中的第一個進程的ID爲1,這個進程同時也是該Namespace中的初始化(init)進程,需要承擔回收孤兒進程等任務。

一個例子

  • ns_child_exec.c 在新的Namespace中執行simple_init.c
  • simple_init.c 新Namespace中的init進程,回收孤兒進程
  • orphan.c 一個孤兒進程示例
    上述三個程序組成了這個示例,在ns_child_exec.c程序中調用clone函數創建子進程並設置Namespace標誌。子進程運行simple_init.c程序,該程序會模擬一個終端,調用execvp命令執行輸入的程序。在這個“終端”中執行orphan程序,可以看到orphan的父進程結束後,子進程成爲孤兒進程並且被simple_init回收。代碼ns_child_exec.c, simple_init.c, orphan.c,代碼中的英文註釋解釋了處理SIGTTOU信號、設置終端前臺進程組等的原因。
    上述程序的執行結果如下:
root@jeffrey: /Coding/namespace# ./ns_child_exec -pmv ./simple_init -v
./ns_child_exec: PID of child created by clone() is 545
        init: my PID is 1
init$ ./orphan
        init: created child 2
Parent (PID=2) created child with PID 3
Parent (PID=2; PPID=1) terminating
        init: SIGCHLD handler: PID 2 terminated
init$
Child (PID=3) now an orphan (parent PID=1)
Child (PID=3) terminating
        init: SIGCHLD handler: PID 3 terminated


init$ ps
        init: created child 4
  PID TTY          TIME CMD
  240 tty2     00:00:00 init
  526 tty2     00:00:00 sudo
  527 tty2     00:00:00 su
  528 tty2     00:00:00 bash
  544 tty2     00:00:00 ns_child_exec
  545 tty2     00:00:00 simple_init
  548 tty2     00:00:00 ps
        init: SIGCHLD handler: PID 4 terminated
init$ mount -t proc proc /proc
        init: created child 5
        init: SIGCHLD handler: PID 5 terminated
init$ ps
        init: created child 6
  PID TTY          TIME CMD
    1 tty2     00:00:00 simple_init
    6 tty2     00:00:00 ps
        init: SIGCHLD handler: PID 6 terminated
init$ ^C./ns_child_exec: terminating
root@jeffrey: /Coding/namespace# 

上述shell命令及輸出結果展示了該程序的使用

  • -pmv參數表示創建新的PID NamespaceMount Namespace,並打印日誌信息
  • 日誌信息init: my PID is 1表明simple_init程序成爲了新PID Namespaceinit進程
  • 執行orphan程序後simple_init打印了兩條SIGCHLD信號處理日誌,表明孤兒進程也被它回收了
  • ps命令依賴/proc中的信息,故若要在執行ps顯示當前PID Namespace的程序,需要顯式掛載它
    • 創建Namespace時需指定創建新Mount NamespacePID Namespace,如-pmv參數
    • 可以通過mount -t proc proc /proc命令掛載
    • 或通過mount("proc", "/proc", "proc", 0, NULL)函數掛載

init進程與信號

linux中的init進程:

  • 只處理設置了處理程序的信號
  • 不會被其它程序或用戶(包括superuser)kill

PID Namespace中的init進程:

  • 類似的,在該Namespace中只會收到設置了處理程序的信號
  • 內核產生的信號依然會發送給init進程,如硬件異常等
  • 對於來自祖先PID Namespace的信號,init進程會收到設置了處理程序的信號+SIGKILL+SIGSTOP
  • SIGKILL(來自祖先)kill後,所有子進程都會被kill

一般來講,一個PID Namespace的所有進程都被終止後,這個PID Namespace也會被銷燬,然而有一個特例:若/proc/PID/ns/pid文件處於bind mounted或打開狀態,則該PID Namespace不會被銷燬。不過,這種情況下無法再在該PID Namespace中創建新的進程(通過setns()+fork()),實際上當執行fork時會失敗併產生ENOMEM錯誤。

unshare() 與 setns()

unshare(CLONE_NEWPID);

fd = open("/proc/PID/ns/pid", O_RDONLY);
setns(fd, 0);

通過在unshare(int flags)的參數中指定CLONE_NEWPID可以創建一個新PID Namespace,調用unshare進程本身不會加入到新PID Namespace中,但由該進程創建的子進程都會被加入其中。其原因在於,幾乎Linux程序包括GNU等都認爲進程標識PID是不變的,若改變當前進程的PID,則許多依賴於此的函數都將無法使用了。

setns(int fd, int nstype)同樣支持PID Namespacefd參數爲一個PID Namespace的文件描述符,該PID Namespace應爲調用進程的PID Namespace的後代。相應文件描述符fd可以通過打開/proc/PID/ns/pid文件獲得。與unshare類似,setns同樣不會講調用進程移動到新的PID Namespace,而是將其子進程放入其中。

又一個例子

本示例展示了setns的使用,使用上一個例子創建一個新的PID Namespace並運行simple_init模擬終端,之後我們在另一個終端中運行ns_run程序,設置子進程的PID Namespacesimple_init的相同,然後創建一個子進程。示例程序,如下爲終端的運行結果。
terminal a:

root@jeffrey: /Coding/namespace# clear
root@jeffrey: /Coding/namespace# ./ns_child_exec -p ./simple_init -v
        init: my PID is 1
init$   init: SIGCHLD handler: PID 4 terminated

terminal b:

root@jeffrey: /Coding/namespace# ps -C simple_init
  PID TTY          TIME CMD
  601 tty3     00:00:00 simple_init
root@jeffrey: /Coding/namespace# ./ns_run -f -n /proc/601/ns/pid ./orphan
Parent (PID=3) created child with PID 4
Parent (PID=3; PPID=0) terminating
root@jeffrey: /Coding/namespace#
Child (PID=4) now an orphan (parent PID=1)
Child (PID=4) terminating

root@jeffrey: /Coding/namespace# 

首先在終端a中執行./ns_child_exec -p ./simple_init -v,之後再終端b中執行./ns_run -f -n /proc/601/ns/pid ./orphan,其中601爲simple_init程序的PID。可以看到,orphan程序創建了孤兒進程PID=4,該進程被simple_init回收了,同時orphan的輸出Parent (PID=3; PPID=0)也表明它在與simple_init相同的Namespace中執行了,因而無法看到位於ns_runNamespace中的父進程表示PPID=0。下圖表明瞭上述代碼的關係:

小結

本文主要講述了PID Namespaceinit進程的角色、unsharesetns等內容,感興趣的同學可以閱讀原文獲得更多相關內容。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章