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