上一篇計算機系統008 - 操作系統概況中講到,計算機操作系統發展的兩個主要方向是提高CPU使用率,以及降低響應時長。這兩者在微觀調度方法一級來看是相互違背的,但從宏觀調度策略來講,還是可以做出折中的利弊權衡。當然,并非所有計算機系統都追求所謂的平衡,很多時候特定領域的系統更重視高實時響應,而有的場景只需要盡可能利用CPU計算能力即可。
方向決定方法,對于進程的理解,就從這兩個目標開始。
1. 進程
進程這個概念忽然跳出來,略顯突兀,可是很多資料上就這么自然地放在這里。為了更好地理解進程概念的由來,這里先對照計算機系統發展史來說明一下。
1.1 進程的由來
最早的時候,也就是單處理器時期,系統要執行一個計算任務的步驟是將整個程序加載到內存中,然后由CPU逐條讀取并執行指令。程序包括代碼指令和預定的數據,指令執行期間,肯定會產生一些計算結果,部分為臨時使用。所以自然而言,寄存器中應當保存一部分臨時值,舉個例子,加法器執行一個加法運算,寄存器中就需要存儲CF進位信息。那么總的來講,一個程序運行后,至少會包含如下信息:
- 程序代碼
- 數據集
- 運行時信息
- 棧
這個時期中計算機系統的計算時間主要通過預約或批處理系統監控獲取,所存在的最大問題是CPU和I/O運行速率量級相差太大,CPU的速率遠遠高過I/O速率,導致一旦執行到I/O指令時,系統中CPU資源就處于閑置狀態,而指令每次執行,又至少會涉及到一次內存地址的訪問。也就是說,系統運行期間,CPU資源被大量浪費。
為了提升CPU資源使用率,降低計算成本,可行的主要方法是加載更多的程序到內存中,一旦某個程序要執行I/O操作,就切換到其他程序運行。當然這里能夠切換的前提是,I/O具體操作由其他單元進行控制,只需少量CPU執行前后準備、收尾工作即可,典型的有DMA直接內存訪問。既然涉及到程序切換,那就勢必要對一個運行中程序的所有相關信息進行統一管理,這樣才能以該單位進行切換避免出錯,而這里,這個單位就叫做進程。
1.2 進程狀態
前面講了將內存中運行中的程序也就是進程進行切換,來達到提升CPU利用率的目的。既然有切換,也就意味著至少有運行、非運行兩種狀態。在該粒度控制下,CPU可以在多個程序間進行切換,避開I/O操作的閑置指令周期,提升整體使用率。
然而上面的調度只是粗粒度的,任何時候,要想做出更精確的決定,就必須基于更多的有效信息。對于操作系統來講,要想更精確地在不同進程間切換CPU,就必須掌握更多進程的有效信息,而增加信息的第一步,就是降低粒度,細化進程狀態的分類。例如對于非運行狀態而言,它難以對新創建的進程和處于I/O等待過程中的進程做區分,這樣一來,將處于I/O等待過程中的重新運行就純屬對CPU資源的浪費行為。
在上述兩種狀態的基礎上,現代操作系統通常會區分為如下幾種狀態:
新建態
就緒態
-
掛起態(可選)
添加掛起態的根本原因是每多加載一個進程進入內存空間就多了一個選擇,就可以更大程度地避開I/O等待,有效利用CPU資源。但由于物理內存有限,導致能加載入內存的進程數量也有限。為了擁有更多可選擇進程,就通過將一部分處于I/O等待過程中的進程從內存空間寫出到磁盤空間,當需要重新調度被寫出進程時,再從磁盤空間重新讀取。
當然也存在其他方法去降低一個進程所需的物理內存,如基于頁交換硬件支持,來實現只加載進程的必要信息而非全部內容來實現降低內存占用。這一部分會再后面的內存管理進一步介紹。- 阻塞/掛起態
- 就緒/掛起態
阻塞/等待態
進程在某些事件發生前不能繼續執行,如I/O操作完成事件。運行態
退出態
1.3 進程調度
如下圖所示,內存(虛擬內存)中進程整體構成如下。進程狀態屬于進程運行時信息中的一部分,現代操作系統中比較通用的運行時狀態信息是一個稱為進程控制塊PCB(Process Control Block)的結構體。
PCB中主要含有三類信息:
進程標識信息
表示信息主要指標識符,包括進程ID(PID)、父進程ID、用戶ID(UID)-
進程狀態信息
包括用戶可見寄存器、控制和狀態寄存器、棧指針三個子類型數據,通常CPU設計中存在一組稱為程序狀態字PSW(Program Status Word)的寄存器,它包含條件碼和其他狀態信息。以Intel X86為例:
進程控制信息
包括調度和狀態信息(如優先級、事件等)、數據結構、進程間通信、進程特權、存儲管理、資源的所有權和使用情況等。
而所謂的各種狀態進程隊列,實際上都是以鏈表形式管理各進程PCB數據結構。
至此我們知道,進程由代碼、數據、棧、運行時信息組成,進程概念的提出主要是便于調度管理,其中所包含的運行時信息可以為調度方法提供有效信息。而調度本身又是通過在處于不同狀態的進程間切換以避開I/O等待,從而達到提高CPU利用率的目的。
2. 線程
上一節中我們從整個操作系統的角度來討論了進程概念的由來和必要性,同時也提到,在具備更多有效信息的基礎上,更小粒度的控制可以達到更加精確的調度結果,從而充分利用CPU。那么對于每一個進程而言,是否有方法可以達成更精細的控制?
答案就是線程。
2.1 線程概念
忽然又跳出來一個名詞,線程。從字面來看,也看不出什么,只好記住一個概念,線程是對進程進行更小粒度的細分。對于單處理器單核系統來講,每次只能運行一個進程,即使進行了更小粒度的細分,也并不能在整體上加快進程運行速度,甚至有可能由于頻繁切換而引入更多的消耗。 但考慮實際應用中,每個程序代碼中可能存在對于多個硬件部件的操作,如一部分進行內部數據計算、一部分負責文件讀寫、一部分負責顯示刷新,最后一部分負責與用戶交互。
這里的用戶交互使用了粗體進行強調,的確,單CPU單核情況下,只能通過不斷切換進程內部執行的指令來提供良好的交互響應。例如,對程序代碼進行兩個部分的劃分,一個部分負責文件讀寫,一部分負責與用戶交互。每個部分以一個線程形式運行,通過將CPU在進程內部的兩個線程間切換,達到執行慢I/O操作的同時,也能插入執行用戶交互代碼。這樣一來,在有限的硬件資源情況下,也一定程度降低了平均響應時長。
對于單核來講,可能見效一般,但是對于多處理器多核來講,將每一個線程分配各同一CPU種的多核后,那就是并行執行多個線程,整體所需時長也將下降。從用戶的角度來講,同樣是降低了程序執行所需平均時長。
所以綜合來看,線程在單核系統上中可以有效降低平均響應時長,在多核系統上,通過并行,可以有效降低整體時長。為了達成精確調度,與進程一樣,線程也應該維護運行時信息。通常多線程技術適用于如下使用場景:
- 前臺和后臺工作
- 異步處理
- 提高執行速度
- 模塊化程序結構
2.2 與進程關系
如上圖所示,一個進程中至少含有一個線程(也稱為主線程),當然也可以根據程序特性選擇使用多線程技術。存在多個線程時,線程間相互共享進程用戶地址空間。
什么意思?換句話就是說,假如內存是整個街道,那么進程就是一套套房子,每套房子相互之間共享道路等資源,就像進程在操作系統中共享總線一樣。如果進程是房子,那么線程就是房子中的房間,各有各的戶型。房間之間相互共享房子內公共資源,如廚房、衛生間等,當然每個房間本身也存在一定內部資源,僅供自己使用。
如果房子創建了,那么肯定至少會創建一個房間。如果房子銷毀了,那么內部所有房間也自然被銷毀。
2.3 線程支持級別
線程的實現上分為用戶級(ULT, User Level Thread)和內核級(KLT, Kernel Level Thread)兩種。
用戶級
線程管理所有工作在應用程序內完成,內核意識不到線程的存在。即應用程序本身擁有絕對自主權,它可以根據程序特點自由使用線程管理策略,同時,也避免了由于某些操作系統內核不支持多線程而無法使用線程技術。
但由于內核未進行支持,內核仍以進程為單位對程序進行調度。因此在線程中調用操作系統API時,一旦發生阻塞,則整個進程都將被阻塞。內核級
對應的有,內核級線程中線程管理工作全部由內核完成,應用程序只需要提交任務為線程即可。與用戶級線程相比,內核掌握了更多線程相關信息,可以將同一個進程調度到不同處理器中,某一個線程的阻塞也不再會阻塞整個進程。
但由于應用程序進程仍處于用戶模式,而線程處于內核模式,因此再講控制從一個線程傳送到同一個進程的另一個線程時,需要進行狀態切換。
由于各有優劣,某些操作系統就混合使用了用戶級、內核級線程,在混合方法中,線程在用戶空間中創建、調度,但也可以托管一部分給內核調度。通過在程序級別與硬件進行匹配,達到最合適的效果。
3. 總結
本篇主要從提高CPU利用率和降低平均響應時長兩個角度來分析了進程、線程概念的由來和必要性,它們均只是內存中的調度單位,創建進程或線程的目的是便于進行下一步的并行執行,也就是在分配給多個CPU或多核或是單核上,按照一定策略調度執行。下一篇中將從并行設計開始說起,看看切換過程中要注意哪些事項。