操作系統那些事(二)

好久沒有寫blog了,原因是最近身體的種種狀況和各種失常。雖然日常工作是做web開發,似乎與與此文毫無關係,但是身在曹營心在漢,出於對系統和網絡的興趣,我更願意去搞清楚計算機是怎麼運行的,網絡是怎樣去互聯的。

本文主要談及信號,殭屍進程,阻塞,非阻塞,同步io,異步io等操作系統相關概念。

一個進程正常退出或者是因某些因素異常退出時,它在用戶空間的數據會被操作系統回收,但是PCB(進程控制塊,屬於內核部分)裏面的數據(包括各種內核相關的東西,比如fd,信號,頁表等)。如果此時操作系統不採取任何措施,那麼這個進程將成爲殭屍進程,殭屍進程佔用系統資源,而且沒有半點作用。注意到,殭屍進程是已經死掉的進程,所以假如嘗試用kill命令去殺死它,是不會有作用的。

應該怎麼樣處理纔不會產生殭屍進程呢?是這樣子的:一般是通過它的父進程把它的相關資源回收了,如果他的父進程都死掉了,那麼就由他的祖先進程(可能是init進程)來回收。所以,如果發現殭屍進程,處理辦法應該是怎樣呢?應該把他的父進程給幹掉,這樣回收工作就會交由祖先進程來處理。

對應到代碼級別,回收資源的處理應該是怎樣的呢?一般有2種方式:通過調用wait或者waitpid函數來回收,wait只能是阻塞調用,waitpid可以是非阻塞的。

解釋幾個重要概念:阻塞和非阻塞,一般情況是系統調用纔會有阻塞非阻塞的說法。阻塞是說進程執行到該系統調用的時候,被暫停執行了,需要等到系統調用返回之後才能繼續執行。非阻塞是執行系統調用的時候,如果該調用因爲某種原因不能立即執行,那麼它會立即返回。舉例來說,假如某進程read網絡上的數據,如果是阻塞io,那麼進程將暫停執行,直到網絡上有數據到達;如果是非阻塞io,如果沒有數據到達,也會立即返回。一般來說磁盤io操作都不會使進程阻塞,但是終端和磁盤io就沒有那麼幸運了,終端io需要阻塞到輸入換行符;網絡需阻塞到有數據到達時。

阻塞和非阻塞都有什麼優缺點呢?阻塞的話,優點是阻塞進程不佔用cpu資源;缺點是進程暫停在某調用中,不能往下繼續執行。非阻塞的話,一般代碼是寫成輪詢的方式,這樣優點的話就是進程可以繼續往下執行,缺點就是佔用太多cpu資源,而且做了太多無用功。

阻塞和非阻塞都有讓人很不爽的缺點,如果想要進程運行得爽一點,而又不會太費cpu資源,可以用信號的方式來處理。

什麼是信號呢?信號屬於PCB中個一個部分,也就是說它位於內核中,一般是用SIG之類的宏定義或者是相應的數字來標識。可以有如下4種方式產生信號:
 通過ctrl+c,鍵盤驅動程序向前臺進程發送一個信號,對後臺進程沒有作用。
 硬件異常產生信號,由內核產生信號,並向相關進程發送此信號。比如進程非法內存時,mmu會引發異常,然後內核產生信號,並將此信號發送給當前進程。
 通過kill命令或者kill函數
 當內核檢測到某種軟件條件發生時,可以通過信號通知進程,比如鬧鐘超時時產生alarm信號。

進程對於信號可以有3種反應:忽略;按默認的動作處理;註冊信號處理函數,當信號發生時,處理函數被執行。這是通過sigaction函數來操作的,通過設置參數,告訴內核,當某信號發生時執行某函數。

有了對信號概念的瞭解之後,可以將wait和waitpid函數以信號通知的方式來執行,因爲信號是異步的,所以當信號發生時(子進程退出後,會產生信號SIGCHLD),進程立即跳轉到相應的信號處理函數執行。這樣一來,就沒有了阻塞操作的不爽(暫停了進程執行),也不會有非阻塞輪詢的cpu消耗。

看起來信號是比較爽,確實是比較爽,但是如果要寫代碼能上臺面的代碼的話,還是需要注意很多細節的,詳情請諮詢APUE。

接下來再談談幾個重要概念。Io是編程裏面非常重要的一類操作。上面曾經提到,磁盤的io是非常爽的,一般來說讀寫都不會阻塞,除非磁盤滿了,或者是被破壞了。但是終端io和網絡io卻不是那麼一回事。因此就有了多種的io模型。

上面已經提及了阻塞io和非阻塞io。這2個對於網絡編程都是不爽的,原因也比較明顯,上文也提及了,不再贅述。因此就出現了多路io,進程可以監聽在多個fd上面,這樣就可以避免因爲某個fd阻塞而使進程無法繼續其他fd的操作,同時也避免了非阻塞io的輪詢。在linux中,此操作對應的系統函數是select,此函數本身是阻塞的,但是它阻塞在多個fd上,只要有一個fd變爲非阻塞,即可以進行io,那麼它就返回。返回之後進程對該fd進行io操作時,就不會阻塞了,這聽起來似乎就比較爽。確實,select函數用得很多,poll函數也差不多,只不過是linux中沒有,某些unix系統中可以使用。

信號io也是使用信號的方式來通知進程,是這樣子的:首先進程告訴內核,當某個fd可以執行io操作時,就麻煩告訴我一下。這樣的話,進程該幹嘛就幹嘛,想幹嘛就幹嘛,不會被阻塞,爽!但是隻能一個進程中只能有一個fd有此待遇,所以我覺得信號io沒有什麼大作用。

以上說的各種io模型,包括阻塞io,非阻塞io,多路io,信號io,都算是同步io。UNP這本書裏是這麼說的。它是這麼解釋的,以上各種io都有一個共性,當內核把數據準備好後,接受數據,也就是將數據從內核拷貝到用戶進程是一個阻塞的操作。這看起來似乎有點奇怪:從內核到用戶進程拷貝數據都算阻塞了,但是是大師說的,我們姑且這麼理解着先。雖然概念是這麼說,但是我覺得沒有必要去糾結這個,理解清楚了各種io操作的過程是怎麼樣的,就差不多了。

真正的異步io操作是怎麼樣子的呢?內核裏面的數據已經拷貝到用戶進程空間,用戶進程只是接收到一個數據已經可用的信號,而無需再執行io操作。這就是所謂的異步io。


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