LINUX PID 1和SYSTEMD 專題

 

 

Linux下有3個特殊的進程,idle進程(PID = 0), init進程(PID = 1)和kthreadd(PID = 2)html

idle進程其pid=0,其前身是系統建立的第一個進程,也是惟一一個沒有經過fork或者kernel_thread產生的進程。shell


各個進程的主要職責:編程

* idle進程(PID = 0,swapper,也叫idle)由系統自動建立, 運行在內核態 ,建立了第一個用戶進程(init進程(PID = 1) /sbin/init)和經過kernel_thread建立第一個內核進程kthreadd(PID = 2) 以後idle進程(PID = 0)進入idle狀態,
當沒有進程能夠被調度的時候運行該進程,不作具體的事情。即 完成加載系統後,演變爲進程調度、交換。ubuntu

*init進程完成系統的初始化. 是系統中全部其它用戶進程的祖先進程。主要做用是處理殭屍進程。當某個父進程比子進程提早消亡時,父進程會給子進程從新尋找「養父進程」,通常就是進程1,由進程1負責處理該子進程的消亡。
   首先Linux內核啓動,而後在用戶空間中啓動init進程,再啓動其餘系統進程。在系統啓動完成完成後,init將變爲守護進程監視系統其餘進程。緩存

*kthreadd進程由idle經過kernel_thread建立,並始終運行在內核空間, 負責全部內核線程的調度和管理 
它的任務就是管理和調度其餘內核線程kernel_thread, 會循環執行一個kthreadd的函數,該函數的做用就是運行kthread_create_list全局鏈表中維護的kthread, 當咱們調用kernel_thread建立的內核線程會被加入到此鏈表中,所以全部的內核線程都是直接或者間接的以kthreadd爲父進程服務器

 

 

 

進程之間的關係:父子關係、兄弟關係
task_struct的 real_parent成員指向父進程,
parent成員指向「養父進程」;
children成員表示該進程的子進程鏈表;
sibling成員表示該進程的兄弟進程鏈表。網絡

 

要說清 Systemd,得先從 Linux 操做系統的啓動提及。Linux 操做系統的啓動首先從 BIOS 開始,而後由 Boot Loader 載入內核,並初始化內核。內核初始化的最後一步就是啓動 init 進程。這個進程是系統的第一個進程,PID 爲 1,又叫超級進程,也叫根進程。它負責產生其餘全部用戶進程。全部的進程都會被掛在這個進程下,若是這個進程退出了,那麼全部的進程都被 kill 。若是一個子進程的父進程退了,那麼這個子進程會被掛到 PID 1 下面。(注:PID 0 是內核的一部分,主要用於內進換頁,參看:Process identifier架構

  SysV Init併發

  PID 1 這個進程很是特殊,其主要就職務是把整個操做系統帶入可操做的狀態。好比:啓動 UI – Shell 以便進行人機交互,或者進入 X 圖形窗口。傳統上,PID 1 和傳統的 Unix System V 相兼容的,因此也叫 sysvinit,這是使用得最悠久的 init 實現。Unix System V 於 1983 年 release。app

  在 sysvint 下,有好幾個運行模式,又叫 runlevel。好比:常見的 3 級別指定啓動到多用戶的字符命令行界面,5 級別指定啓起到圖形界面,0 表示關機,6 表示重啓。其配置在 /etc/inittab 文件中。

  與此配套的還有 /etc/init.d/ 和 /etc/rc[X].d,前者存放各類進程的啓停腳本(須要按照規範支持 startstop子命令),後者的 X 表示不一樣的 runlevel 下相應的後臺進程服務,如:/etc/rc3.d 是 runlevel=3 的。 裏面的文件主要是 link 到  /etc/init.d/ 裏的啓停腳本。其中也有必定的命名規範:S 或 K 打頭的,後面跟一個數字,而後再跟一個自定義的名字,如:S01rsyslogS02ssh。S 表示啓動,K表示中止,數字表示執行的順序。

  UpStart

  Unix 和 Linux 在 sysvint 運做多年後,大約到了 2006 年的時候,Linux 內核進入 2.6 時代,Linux 有了不少更新。而且,Linux 開始進入桌面系統,而桌面系統和服務器系統不同的是,桌面系統面臨頻繁重啓,並且,用戶會很是頻繁的使用硬件的熱插拔技術。因而,這些新的場景,讓 sysvint 受到了不少挑戰。

  好比,打印機須要 CUPS 等服務進程,可是若是用戶沒有打機印,啓動這個服務徹底是一種浪費,而若是不啓動,若是要用打印機了,就沒法使用,由於sysvint 沒有自動檢測的機制,它只能一次性啓動全部的服務。另外,還有網絡盤掛載的問題。在 /etc/fstab中,負責硬盤掛載,有時候還有網絡硬盤(NFS 或 iSCSI)在其中,可是在桌面機上,有極可能開機的時候是沒有網絡的, 因而網絡硬盤都不能夠訪問,也沒法掛載,這會極大的影響啓動速度。sysvinit 採用 netdev 的方式來解決這個問題,也就是說,須要用戶本身在 /etc/fstab 中給相應的硬盤配置上 netdev 屬性,因而 sysvint 啓動時不會掛載它,只有在網絡可用後,由專門的 netfs 服務進程來掛載。這種管理方式比較難以管理,也很容易讓人掉坑。

  因此,Ubuntu 開發人員在評估了當時幾個可選的 init 系統後,決定從新設計這個系統,因而,這就是咱們後面看到的 upstart 。 upstart 基於事件驅動的機制,把以前的徹底串行的同步啓動服務的方式改爲了由事件驅動的異步的方式。好比:若是有U盤插入,udev 獲得通知,upstart 感知到這個事件後觸發相應的服務程序,好比掛載文件系統等等。由於使用一個事件驅動的玩法,因此,啓動操做系統時,不少沒必要要的服務能夠不用啓動,而是等待通知,lazy 啓動。並且事件驅動的好處是,能夠並行啓動服務,他們之間的依賴關係,由相應的事件通知完成。

  upstart 有着很不錯的設計,其中最重要的兩個概念是 Job 和 Event。

  Job 有通常的 Job,也有 service 的 Job,而且,upstart 管理了整個 Job 的生命週期,好比:Waiting, Starting, pre-Start, Spawned, post-Start, Running, pre-Stop, Stopping, Killed, post-Stop 等等,並維護着這個生命週期的狀態機。

  Event 分紅三類,signalmethod 和 hookssignal 就是異步消息,method 是同步阻塞的。hooks 也是同步的,但介於前面二者之間,發出 hook 事件的進程必須等到事件完成,但不檢查是否成功。

  可是,upstart 的事件很是複雜,也很是紛亂,各類各樣的事件(事件沒有歸好類)致使有點凌亂。不過由於整個事件驅動的設計比以前的 sysvinit 來講好太多,因此,也深得歡迎。

  Systemd

  直到 2010 的有一天,一個在 RedHat 工做的工程師 Lennart Poettering 和 Kay Sievers ,開始引入了一個新的 init 系統—— systemd。這是一個很是很是有野心的項目,這個項目幾乎改變了全部的東西,systemd 不但想取代已有的 init 系統,並且還想幹更多的東西。

  Lennart 贊成 upstart 乾的不錯,代碼質量很好,基於事件的設計也很好。可是他以爲 upstart 也有問題,其中最大的問題仍是不夠快,雖然 upstart 用事件能夠達到必定的啓動並行度,可是,本質上來講,這些事件仍是會讓啓動過程串行在一塊兒。  如:NetworkManager 在等 D-Bus 的啓動事件,而 D-Bus 在等 syslog 的啓動事件。

  Lennart 認爲,實現上來講,upstart 實際上是在管理一個邏輯上的服務依賴樹,可是這個服務依賴樹在表現形式上比較簡單,你只須要配置——「啓動 B 好了就啓動A」或是「中止了A後就中止B」這樣的規則。可是,Lennart 說,這種簡單實際上是有害的(this simplification is actually detrimental)。他認爲,

  • 從一個系統管理的角度出來,他一開始會設定好整個系統啓動的服務依賴樹,可是這個系統管理員要人肉的把這個原本就很是乾淨的服務依整樹給翻譯成計算機看的懂的 Event/Action 形式,並且 Event/Action 這種配置方式是運行時的,因此,你須要運行起來才知道是什麼樣的。
  • Event 邏輯從頭到腳處處都是,這個事件擴大了運維的複雜度,還不如以前的 sysvint。 也就是說,當用戶配置了 「啓動 D-Bus 後請啓動 NetworkManager」, 這個 upstart 能夠幹,可是反過來,若是,用戶啓動 NetworkManager,咱們應該先去啓動他的前置依賴 D-Bus,然而你還要配置相應的反向 Event。原本,我只須要配置一條依賴的,結果如今我要配置不少不少狀況下的 Event。
  • 最後,upstart 裏的 Event 的並不標準,很混亂,沒有良好的定義。好比:既有,進程啓動,運行,中止的事件,也有 USB 設備插入、可用、拔出的事件,還有文件系統設備 being mounted、 mounted 和 umounted 的事件,還有 AC 電源線鏈接和斷開的事件。你會發現,這進程啓停的、USB 的、文件系統的、電源線的事件,看上去長得很像, 可是沒有被標準化抽像出來掉,由於絕大多數的事件都是三元組:start, condition, stop 。這種概念設計模型並無在 upstart 中出現。由於 upstart 被設計爲單一的事件,而忽略了邏輯依賴。

  固然,若是 systemd 只是解決 upstart 的問題,他就改造 upstart 就行了,可是 Lennart 的野心不僅是想幹個這樣的事,他想幹的更多。

  首先,systemd 清醒的認識到了 init 進程的首要目標是要讓用戶快速的進入能夠操做 OS 的環境,因此,這個速度必定要快,越快越好,因此,systemd 的設計理念就是兩條:

  • To start less.
  • And to start more in parallel.

  也就是說,按需啓動,能不啓動就不啓動,若是要啓動,能並行啓動就並行啓動,包括大家之間有依賴,我也並行啓動。按需啓動還好理解,那麼,有依賴關係的並行啓動,它是怎麼作到的?這裏,systemd 借鑑了 MacOS 的 Launchd 的玩法(在 Youtube 上有一個分享——Launchd: One Program to Rule them All,在蘋果的開源網站上也有相關的設計文檔——About Daemons and Services

  要解決這些依賴性,systemd 須要解決好三種底層依賴—— Socket, D-Bus ,文件系統。

  • Socket 依賴。若是服務C依賴於服務S的 socket,那麼就要先啓動S,而後再啓動C,由於若是C啓動時找不到S的 Socket,那麼C就會失敗。systemd 能夠幫你在S尚未啓動好的時候,創建一個 socket,用來接收全部的C的請求和數據,並緩存之,一旦S所有啓動完成,把 systemd 替換好的這個緩存的數據和 Socket 描述符替換過去。
  • D-Bus 依賴D-Bus 全稱 Desktop Bus,是一個用來在進程間通訊的服務。除了用於用戶態進程和內核態進程通訊,也用於用戶態的進程以前。如今,不少的如今的服務進程都用 D-Bus 而不是 Socket 來通訊。好比:NetworkManager 就是經過 D-Bus 和其它服務進程通信的,也就是說,若是一個進程須要知道網絡的狀態,那麼就必須要經過 D-Bus 通訊。D-Bus 支持 「Bus Activation」的特性。也就是說,A要經過 D-Bus 服務和B通信,可是B沒有啓動,那麼 D-Bus 能夠把B起來,在B啓動的過程當中,D-Bus 幫你緩存數據。systemd 能夠幫你利用好這個特性來並行啓動 A 和 B。
  • 文件系統依賴。系統啓動過程當中,文件系統相關的活動是最耗時的,好比掛載文件系統,對文件系統進行磁盤檢查(fsck),磁盤配額檢查等都是很是耗時的操做。在等待這些工做完成的同時,系統處於空閒狀態。那些想使用文件系統的服務彷佛必須等待文件系統初始化完成才能夠啓動。systemd 參考了 autofs 的設計思路,使得依賴文件系統的服務和文件系統自己初始化二者能夠併發工做。autofs 能夠監測到某個文件系統掛載點真正被訪問到的時候才觸發掛載操做,這是經過內核 automounter 模塊的支持而實現的。好比一個 open ()系統調用做用在 「/misc/cd/file1」 的時候,/misc/cd 還沒有執行掛載操做,此時 open () 調用被掛起等待,Linux 內核通知 autofsautofs 執行掛載。這時候,控制權返回給 open () 系統調用,並正常打開文件。

  下圖來自 Lennart 的演講裏的一頁 PPT,展現了不一樣 init 系統的啓動。

  除此以外,systemd 還在啓動時管理好了一些下面的事。

  用C語言取代傳統的腳本式的啓動。前面說過,sysvint 用 /etc/rcX.d 下的各類腳本啓動。然而這些腳本中須要使用 awksedgrepfindxargs 等等這些操做系統的命令,這些命令須要生成進程,生成進程的開銷很大,關鍵是生成完這些進程後,這個進程就幹了點屁大的事就退了。換句話說就是,我操做系統幹了那麼多事爲你拉個進程起來,結果你就把個字串轉成小寫就退了,把我操做系統當什麼了?

  在正常的一個 sysvinit 的腳本里,可能會有成百上千個這樣的命令。因此,慢死。所以,systemd 全面用 C 語言所有取代了。通常來講,sysvinit 下,操做系統啓動完成後,用 echo $$ 能夠看到,pid 被分配到了上千的樣子,而 systemd 的系統只是上百。

  另外,systemd 是真正一個能夠管住服務進程的——能夠跟蹤上服務進程所 fork/exec 出來的全部進程。

  • 咱們知道, 傳統 Unix/Linux 的 Daemon 服務進程的最佳實踐基本上是這個樣子的 (具體過程可參看這篇文章「SysV Daemon」)——
    1. 進程啓動時,關閉全部的打開的文件描述符(除了標準描述符0,1,2),而後重置全部的信號處理。
    2. 調用 fork () 建立子進程,在子進程中 setsid (),而後父進程退出(爲了後臺執行)
    3. 再子程中,再調用一次 fork (),建立孫子進程,肯定沒有交互終端。而後子進程退出。
    4. 在孫子進程中,把標準輸入標準輸出標準錯誤都連到 /dev/null 上,還要建立 pid 文件,日誌文件,處理相關信號 ……
    5. 最後纔是真正開始提供服務。
  • 在上面的這個過程當中,服務進程除了兩次 fork 外還會 fork 出不少不少的子進程(好比說一些 Web 服務進程,會根據用戶的請求連接來 fork 子進程),這個進程樹是至關難以管理的,由於,一旦父進程退出來了,子進程就會被掛到 PID 1 下,因此,基本上來講,你沒法經過服務進程自已給定的一個 pid 文件來找到全部的相關進程(這個對開發者的要求過高了),因此,在傳統的方式下用腳本啓停服務是至關至關的 Buggy 的,由於沒法作對全部的服務生出來的子子孫孫作到監控。
  • 爲了解決這個問題,upstart 經過變態的 strace 來跟蹤進程中的 fork () 和 exec () 或 exit () 等相關的系統調用。這種方法至關笨拙。 systemd 使用了一個很是有意思的玩法來 tracking 服務進程生出來的全部進程,那就是用 cgroup (我在 Docker 的基礎技術「cgroup 篇」中講過這個東西)。cgroup 主要是用來管理進程組資源配額的事,因此,不管服務如何啓動新的子進程,全部的這些相關進程都會同屬於一個 cgroup,因此,systemd 只須要簡單的去遍歷一下相應的 cgroup 的那個虛文件系統目錄下的文件,就能夠正確的找到全部的相關進程,並將他們一一中止。

  另外,systemd 簡化了整個 daemon 開發的過程:

  • 不須要兩次 fork (),只須要實現服務自己的主邏輯就能夠了。
  • 不須要 setsid ()systemd 會幫你幹
  • 不須要維護 pid 文件systemd 會幫處理。
  • 不須要管理日誌文件或是使用syslog,或是處理HUP的日誌 reload 信號。把日誌打到 stderr 上,systemd 幫你管理。
  • 處理 SIGTERM 信號,這個信號就是正確退出當前服務,不要作其餘的事。
  • ……

  除此以外,systemd 還能——

  • 自動檢測啓動的服務間有沒有環形依賴。
  • 內建 autofs 自動掛載管理功能。
  • 日誌服務。systemd 改造了傳統的 syslog 的問題,採用二進制格式保存日誌,日誌索引更快。
  • 快照和恢復。對當前的系統運行的服務集合作快照,並能夠恢復。
  • ……

  還有好多好多,他接管不少不少東西,因而就讓不少人不爽了,由於他在幹了不少本不屬於 PID 1 的事。

  Systemd 爭論和八卦

  因而 systemd 這個東西成了多是有史以來口水戰最多的一個開源軟件了。systemd 飽受各類爭議,最大的爭議就是他破壞了 Unix 的設計哲學(相關的哲學能夠讀一下《Unix 編程藝術》),幹了一個大而全並且至關複雜的東西。固然,Lennart 並不一樣意這樣的說法,他後來又寫一篇 blog 「The Biggest Myths」來解釋 systemd 並非這樣的,你們能夠前往一讀。

  這個爭議大到什麼樣子呢?2014 年,Debian Linux 由於想準備使用 systemd 來做爲標準的 init 守護進程來替換 sysvinit 。而圍繞這個事的爭論達到了空前的熱度,爭論中充滿着仇恨,systemd 的支持者和反對者都在互相辱罵,致使當時 Debian 陣營開始分裂。還有人給 Lennart 發了死亡威脅的郵件,用比特幣僱兇買殺手,揚言要取他的性命,在 Youbute 上傳了侮辱他的歌曲,在 IRC 和各類社交渠道上給他發下流和侮辱性的消息。這已經不是爭議了,而是一種徹徹底底的仇恨!

  因而,Lennart 在 Google Plus 上發了貼子,批評整個 Linux 開源社區和 Linus 本人。他大意說,

這個社區太病態了,全是 ass holes,大家不停用各類手段在各類地方用不一樣的語言和方式來侮辱和漫罵我。我仍是一個年輕人,我歷來沒有經歷過這樣的場面,可是今天我已經對這種場面很熟悉了。我有時候說話可能不許確,可是我不會像他樣那樣說出那樣的話,我也沒有被這些事影響,由於我臉皮夠厚,因此,爲何我能夠在如何大的反對聲面前讓 systemd 成功,可是,大家 Linux 社區太可怕了。大家裏面的有精神病的人太多了。另外,對於 Linus Torvalds,你是這個社區的 Role Model,但惋惜你是一個 Bad Role Model,你在社區裏的刻薄和侮辱性的言行,基本從必定程度上鼓勵了其它人跟你同樣,固然,並不僅是你一我的的問題,而是在你周圍彙集了一羣和你同樣的這樣乾的人。
送你一句話—— 
A fish rots from the head down !
一條魚是從頭往下腐爛的……

  這篇契文很長,喜歡八卦的同窗能夠前往一讀。感覺一下 Lennart 當時的心態(我以爲能算上是很是平穩了)。

  Linus 也在被一媒體問起 systemd 這個事來(參看「Torvalds says he has no strong opinions on systemd」),
       Linus 在採訪裏說,

我對 systemd 和 Lennart 的貼子沒有什麼強烈的想法。雖然,傳統的 Unix 設計哲學—— 「Do one thing and Do it well」,很不錯,並且咱們大多數人也實踐了這麼多年,可是這並不表明全部的真實世界。在歷史上,也不僅有systemd 這麼幹過。可是,我我的仍是 old-fashioned 的人,至少我喜歡文本式的日誌,而不是二進制的日誌。可是 systemd 沒有必要必定要有這樣的品味。哦,我說細節了……

  今天,systemd 佔據了幾乎全部的主流的 Linux 發行版的默認配置,包括:Arch Linux、CentOS、CoreOS、Debian、Fedora、Megeia、OpenSUSE、RHEL、SUSE 企業版和 Ubuntu。並且,對於 CentOS, CoreOS, Fedora, RHEL, SUSE 這些發行版來講,不能沒有 systemd。(Ubuntu 還有一個不錯的 wiki – Systemd for Upstart Users 闡述瞭如何在二者間切換)

  其它

  還記得在《緩存更新的套路》一文中,我說過,若是你要作好架構,首先你得把計算機體系結構以及不少老古董的基礎技術吃透了。由於裏面會有不少能夠借鑑和相通的東西。那麼,你是否從這篇文章裏看到了一些有分佈式架構類似的東西?

  好比:從 sysvinit 到 upstart 再到 systemd,像不像是服務治理?Linux 系統下的這些服務進程,是否是很像分佈式架構中的微服務?還有那個D-Bus,是否是很像 SOA 裏的 ESB?而 init 系統是否是很像一個控制系統?甚至像一個服務編排(Service Orchestration)系統?

  分佈式系統中的服務之間也有不少依賴,因此,在啓動一個架構的時候,若是咱們能夠作到像 systemd 那樣並行啓動的話,那麼是否是就像是一個微服務的玩法了?

  嗯,你會發現,技術上的不少東西是相通的,也是互相有對方的影子,因此,其實技術並很少。關鍵是咱們學在了表面仍是看到了本質。

https://news.cnblogs.com/n/573985/

相關文章
相關標籤/搜索