操作系统-线程
說(shuō)明:文中內(nèi)容大部分都是大部分都是《操作系統(tǒng)-精髓與設(shè)計(jì)原理 第八版》的原文,自己做了一些刪改,使其更易于理解。
本章講述一些與進(jìn)程管理相關(guān)的高級(jí)概念,這些概念在很多現(xiàn)代操作系統(tǒng)中都可以找到。實(shí)際上,它包含了兩個(gè)獨(dú)立的概念:一個(gè)與資源所有權(quán)有關(guān),一個(gè)與執(zhí)行相關(guān)。這一區(qū)別使得許多操作系統(tǒng)中出現(xiàn)和發(fā)展了稱(chēng)為線程(thread)的結(jié)構(gòu)。
一 進(jìn)程和線程
在迄今為止的討論中,進(jìn)程具有如下兩個(gè)特點(diǎn):
- 資源所有權(quán)
- 調(diào)度/執(zhí)行
這兩個(gè)特點(diǎn)是獨(dú)立的,因此操作系統(tǒng)應(yīng)能分別處理它們。很多操作系統(tǒng),特別是近期開(kāi)發(fā)的操作系統(tǒng)已在這樣做。為區(qū)分這兩個(gè)特點(diǎn),我們通常將分派的單位稱(chēng)為線程或輕量級(jí)進(jìn)程(Light Weight Process,LWP),而將擁有資源所有權(quán)的單位稱(chēng)為進(jìn)程(process)或任務(wù)(task)。
1.1 多線程
多線程是指操作系統(tǒng)在單個(gè)進(jìn)程內(nèi)支持多個(gè)并發(fā)執(zhí)行路徑的能力。每個(gè)進(jìn)程中僅執(zhí)行單個(gè)線程的傳統(tǒng)方法(此時(shí)還未提出線程的概念)稱(chēng)為單線程方法(single-threaded approach)。
在多線程環(huán)境中,進(jìn)程定義為資源分配單元和一個(gè)保護(hù)單元。與進(jìn)程相關(guān)聯(lián)的有:
? ·容納進(jìn)程映像的虛擬地址空間。
? ·對(duì)處理器、其他進(jìn)程(用于進(jìn)程間通信)、文件和I/O資源(設(shè)備和通道)的受保護(hù)訪問(wèn)。
一個(gè)進(jìn)程中可能有一個(gè)或多個(gè)線程,每個(gè)線程都有:
? 一個(gè)線程執(zhí)行狀態(tài)(運(yùn)行、就緒等)。
? 未運(yùn)行時(shí)保存的線程上下文;線程可視為在進(jìn)程內(nèi)運(yùn)行的一個(gè)獨(dú)立程序計(jì)數(shù)器。
? 一個(gè)執(zhí)行棧。
? 每個(gè)線程用于局部變量的一些靜態(tài)存儲(chǔ)空間。
? 與進(jìn)程內(nèi)其他線程共享的內(nèi)存和資源的訪問(wèn)。
— 單線程多線程進(jìn)程模型
圖4.2從進(jìn)程管理的角度說(shuō)明了線程和進(jìn)程的區(qū)別。
在單線程進(jìn)程模型中(無(wú)明確的線程概念),進(jìn)程的表示包括其進(jìn)程控制塊和用戶(hù)地址空間,以及在進(jìn)程執(zhí)行中管理調(diào)用/返回行為的用戶(hù)棧和內(nèi)核棧。進(jìn)程正運(yùn)行時(shí),處理器寄存器由該進(jìn)程控制;進(jìn)程未運(yùn)行時(shí),將保存這些處理器寄存器中的內(nèi)容。
在多線程環(huán)境中 進(jìn)程仍然只有一個(gè)與之關(guān)聯(lián)的進(jìn)程控制塊和用戶(hù)地址空間,但每個(gè)線程現(xiàn)在會(huì)有許多單獨(dú)的棧和一個(gè)單獨(dú)的控制塊,控制塊中包含寄存器值、優(yōu)先級(jí)和其他與線程相關(guān)的狀態(tài)信息。
因此,進(jìn)程中的所有線程共享該進(jìn)程的狀態(tài)和資源,所有線程都駐留在同一塊地址空間中,并可訪問(wèn)相同的數(shù)據(jù)。當(dāng)某個(gè)線程改變了內(nèi)存中的一個(gè)數(shù)據(jù)項(xiàng)時(shí),其他線程在訪問(wèn)這一數(shù)據(jù)項(xiàng)時(shí)會(huì)看到這一變化。若一個(gè)線程以讀權(quán)限打開(kāi)一個(gè)文件,那么同一進(jìn)程中的其他線程也能從這個(gè)文件中讀取數(shù)據(jù)。
— 比較性能后會(huì)發(fā)現(xiàn)線程的如下優(yōu)點(diǎn):
1.在已有進(jìn)程中創(chuàng)建一個(gè)新線程的時(shí)間,遠(yuǎn)少于創(chuàng)建一個(gè)全新進(jìn)程的時(shí)間。Mach開(kāi)發(fā)人員的研究表明,線程創(chuàng)建要比在UNIX中創(chuàng)建進(jìn)程快10倍。
2.終止線程要比終止進(jìn)程所花的時(shí)間少。
3.同一進(jìn)程內(nèi)線程間切換的時(shí)間,要少于進(jìn)程間切換的時(shí)間。
4.線程提高了不同執(zhí)行程序間通信的效率。在多數(shù)操作系統(tǒng)中,獨(dú)立進(jìn)程間的通信需要內(nèi)核介入,以提供保護(hù)和通信所需的機(jī)制。但是,由于同一進(jìn)程中的多個(gè)線程共享內(nèi)存和文件,因此它們無(wú)須調(diào)用內(nèi)核就可互相通信。
總結(jié)起來(lái)就是創(chuàng)建、終止、切換時(shí)間和通信效率的提高。
因此,若將一個(gè)應(yīng)用程序或函數(shù)實(shí)現(xiàn)為一組相關(guān)聯(lián)的執(zhí)行單元,則用一組線程要比用一組分離的進(jìn)程更有效。此外,在單處理器上使用線程結(jié)構(gòu),但其可簡(jiǎn)化邏輯上從事幾種不同工作的程序的結(jié)構(gòu)。
— 這里給出在單用戶(hù)多處理系統(tǒng)中使用線程的4個(gè)例子:
- 前臺(tái)后臺(tái)工作
- 異步執(zhí)行
- 執(zhí)行速度:一個(gè)線程在讀取數(shù)據(jù)時(shí)被I/O操作阻塞,另一個(gè)線程仍然可以繼續(xù)運(yùn)行。
- 模塊化程序結(jié)構(gòu)
在支持線程的操作系統(tǒng)中,調(diào)度和分派是在線程基礎(chǔ)上完成的,因此大多數(shù)與執(zhí)行相關(guān)的信息可以保存在線程級(jí)的數(shù)據(jù)結(jié)構(gòu)中。但是,有些活動(dòng)會(huì)影響進(jìn)程中的所有線程,因此操作系統(tǒng)必須在進(jìn)程級(jí)對(duì)它們進(jìn)行管理。例如,掛起操作會(huì)把一個(gè)進(jìn)程的地址空間換出內(nèi)存,以便為其他進(jìn)程的地址空間騰出位置。因?yàn)橐粋€(gè)進(jìn)程中的所有線程共享同一個(gè)地址空間,因此它們會(huì)同時(shí)被掛起。類(lèi)似地,進(jìn)程終止時(shí)會(huì)使得進(jìn)程中的所有線程都終止。
1.2 線程功能
類(lèi)似于進(jìn)程,線程也具有執(zhí)行狀態(tài),且可彼此同步。下面依次介紹線程的這兩種功能。
— 線程狀態(tài)
和進(jìn)程一樣,線程的主要狀態(tài)有運(yùn)行態(tài)、就緒態(tài)和阻塞態(tài)。一般來(lái)說(shuō),掛起態(tài)對(duì)線程沒(méi)有意義,因此這類(lèi)狀態(tài)僅適用于進(jìn)程。特別地,如果一個(gè)進(jìn)程被換出,由于所有線程都共享該進(jìn)程的地址空間,因此所有線程都須被換出。
有4種與線程狀態(tài)改變相關(guān)的基本操作:
- 派生:典型情況下,在派生一個(gè)新進(jìn)程時(shí),同時(shí)也會(huì)為該進(jìn)程派生一個(gè)線程。隨后,進(jìn)程中的線程可在同一進(jìn)程中派生另一個(gè)線程,并為新線程提供指令指針和參數(shù);新線程擁有自己的寄存器上下文和棧空間,并放在就緒隊(duì)列中。
- 阻塞:線程需要等待一個(gè)事件時(shí)會(huì)被阻塞(保存線程的用戶(hù)寄存器、程序計(jì)數(shù)器和棧指針),處理器轉(zhuǎn)而執(zhí)行另一個(gè)就緒線程。
- 解除阻塞:發(fā)生阻塞一個(gè)線程的事件時(shí),會(huì)將該線程轉(zhuǎn)移到就緒隊(duì)列中。
- 結(jié)束:一個(gè)線程完成后,會(huì)釋放其寄存器上下文和棧。
— 線程同步
一個(gè)進(jìn)程中的所有線程共享同一個(gè)地址空間和諸如打開(kāi)的文件之類(lèi)的其他資源。
一個(gè)線程對(duì)資源的任何修改都會(huì)影響同一進(jìn)程中其他線程的環(huán)境,因此需要同步各種線程的活動(dòng),以便它們互不干擾且不破壞數(shù)據(jù)結(jié)構(gòu)。例如,兩個(gè)線程都試圖同時(shí)往一個(gè)雙向鏈表中增加一個(gè)元素時(shí),可能會(huì)丟失一個(gè)元素或破壞鏈表結(jié)構(gòu)。
二 線程分類(lèi)
2.1 用戶(hù)級(jí)和內(nèi)核級(jí)線程
線程分為兩大類(lèi),即用戶(hù)級(jí)線程(User-Level Thread,ULT)和內(nèi)核級(jí)線程(Kernel-Level Thread,KLT),后者又稱(chēng)內(nèi)核支持的線程或輕量級(jí)進(jìn)程。
用戶(hù)級(jí)線程
在純ULT軟件中,管理線程的所有工作都由應(yīng)用程序完成,內(nèi)核意識(shí)不到線程的存在。圖4.5(a)說(shuō)明了純ULT方法。任何應(yīng)用程序都可使用線程庫(kù)設(shè)計(jì)成多線程程序。線程庫(kù)是管理用戶(hù)級(jí)線程的一個(gè)例程包,它含有創(chuàng)建和銷(xiāo)毀線程的代碼、在線程間傳遞消息和數(shù)據(jù)的代碼、調(diào)度線程執(zhí)行的代碼,以及保存和恢復(fù)線程上下文的代碼。
默認(rèn)情況下,應(yīng)用程序從單個(gè)線程開(kāi)始,并在該線程中開(kāi)始運(yùn)行。這個(gè)應(yīng)用程序及其線程將分配給一個(gè)由內(nèi)核管理的進(jìn)程。應(yīng)用程序在運(yùn)行(進(jìn)程處于運(yùn)行態(tài))的任何時(shí)刻,都可派生一個(gè)在相同進(jìn)程中運(yùn)行的新線程。線程派生是通過(guò)調(diào)用線程庫(kù)中的派生例程實(shí)現(xiàn)的。通過(guò)過(guò)程調(diào)用,控制權(quán)傳遞給派生例程。線程庫(kù)為新線程創(chuàng)建一個(gè)數(shù)據(jù)結(jié)構(gòu),然后使用某種調(diào)度算法,把控制權(quán)傳遞給該進(jìn)程中處于就緒態(tài)的一個(gè)線程。當(dāng)控制權(quán)傳遞給線程庫(kù)時(shí),需要保存當(dāng)前線程的上下文,然后在控制權(quán)從線程庫(kù)中傳遞給一個(gè)線程時(shí),恢復(fù)那個(gè)線程的上下文。上下文實(shí)際上包括用戶(hù)寄存器的內(nèi)容、程序計(jì)數(shù)器和棧指針。
上段描述的所有活動(dòng)發(fā)生在用戶(hù)空間中和一個(gè)進(jìn)程內(nèi),內(nèi)核并不知道這些活動(dòng)。內(nèi)核繼續(xù)以進(jìn)程為單位進(jìn)行調(diào)度,并為進(jìn)程指定一個(gè)執(zhí)行狀態(tài)(就緒態(tài)、運(yùn)行態(tài)、阻塞態(tài)等)。下面的例子將闡述線程調(diào)度和進(jìn)程調(diào)度的關(guān)系。假設(shè)進(jìn)程B在其線程2中執(zhí)行,進(jìn)程和作為進(jìn)程一部分的兩個(gè)用戶(hù)級(jí)線程的狀態(tài)如圖4.6(a)所示。此時(shí)可能會(huì)發(fā)生如下情況之一:
- 在線程2中執(zhí)行的應(yīng)用程序代碼進(jìn)行一個(gè)阻塞進(jìn)程B的系統(tǒng)調(diào)用。例如,執(zhí)行一次I/O調(diào)用。這會(huì)把控制權(quán)轉(zhuǎn)交給內(nèi)核,隨后內(nèi)核啟動(dòng)I/O操作,把進(jìn)程B置于阻塞態(tài),并切換到另一個(gè)進(jìn)程。在此期間,根據(jù)線程庫(kù)維護(hù)的數(shù)據(jù)結(jié)構(gòu),進(jìn)程B的線程2仍處于運(yùn)行狀態(tài)。注意,從在處理器上執(zhí)行的角度來(lái)看,線程2實(shí)際上并不處于運(yùn)行態(tài),只是在線程庫(kù)看來(lái)它處于運(yùn)行態(tài)。
- 時(shí)鐘中斷把控制權(quán)傳遞給內(nèi)核,內(nèi)核確定當(dāng)前正運(yùn)行的進(jìn)程B已用完其時(shí)間片。內(nèi)核把進(jìn)程B置于就緒態(tài)并切換到另一個(gè)進(jìn)程。同時(shí),根據(jù)線程庫(kù)維護(hù)的數(shù)據(jù)結(jié)構(gòu),進(jìn)程B的線程2仍處于運(yùn)行態(tài)。
- 線程2運(yùn)行到需要進(jìn)程B的線程1執(zhí)行某些動(dòng)作的一個(gè)點(diǎn)。此時(shí),線程2進(jìn)入阻塞態(tài),而線程1從就緒態(tài)轉(zhuǎn)換到運(yùn)行態(tài)。進(jìn)程自身保留在運(yùn)行態(tài)。
在線程的調(diào)度過(guò)程中,可以認(rèn)為進(jìn)程一直是在運(yùn)行之中的。因?yàn)樵谶M(jìn)程不在運(yùn)行的時(shí)候,可以認(rèn)為進(jìn)程內(nèi)部一切都停止了。當(dāng)進(jìn)程再次運(yùn)行,進(jìn)程內(nèi)部才繼續(xù)進(jìn)行。
— 使用ULT而非KLT的優(yōu)點(diǎn)如下:
總結(jié):減少模式切換開(kāi)銷(xiāo)、自定義調(diào)度算法、操作系統(tǒng)平臺(tái)無(wú)關(guān)
— 使用ULT而非KLT的缺點(diǎn)如下:
總結(jié):一個(gè)線程阻塞,則同一個(gè)進(jìn)程中所有線程阻塞;無(wú)法使用多處理器技術(shù)
— 現(xiàn)在已有解決這兩個(gè)問(wèn)題的方法
解決問(wèn)題1
使用一種稱(chēng)為“套管”(jacketing)的技術(shù)。“套管”的目標(biāo)是把一個(gè)產(chǎn)生阻塞的系統(tǒng)調(diào)用轉(zhuǎn)化為一個(gè)非阻塞的系統(tǒng)調(diào)用。例如,替代直接調(diào)用一個(gè)系統(tǒng)I/0例程,讓線程調(diào)用一個(gè)應(yīng)用級(jí)的I/O套管例程,這個(gè)套管例程中的代碼用于檢查并確定I/O設(shè)備是否忙。如果忙,該線程進(jìn)入阻塞態(tài)并把控制權(quán)傳送給另一個(gè)線程。這個(gè)線程重新獲得控制權(quán)后,套管例程會(huì)再次檢查I/O設(shè)備。
這就有點(diǎn)像線程提示操作系統(tǒng)產(chǎn)生一個(gè)進(jìn)程,這個(gè)新進(jìn)程去執(zhí)行I/O操作,執(zhí)行完成后提示這個(gè)線程,在完成之前,這個(gè)線程都是阻塞,但是原來(lái)的進(jìn)程就可以執(zhí)行別的線程而不被阻塞。
解決問(wèn)題2
把應(yīng)用程序?qū)懗梢粋€(gè)多進(jìn)程程序而非多線程程序。但是,這種方法消除了線程的主要優(yōu)點(diǎn):每次切換都變成進(jìn)程間的切換而非線程間的切換,導(dǎo)致開(kāi)銷(xiāo)過(guò)大。
內(nèi)核級(jí)線程
在純KLT軟件中,管理線程的所有工作均由內(nèi)核完成。應(yīng)用級(jí)沒(méi)有線程管理代碼,只有一個(gè)到內(nèi)核線程設(shè)施的應(yīng)用編程接口(API)。Windows是這種方法的一個(gè)例子。
圖4.5(b)顯示了純KLT方法。內(nèi)核為進(jìn)程及進(jìn)程內(nèi)的每個(gè)線程維護(hù)上下文信息。調(diào)度由內(nèi)核基于線程完成。
這種方法克服了ULT方法的兩個(gè)缺點(diǎn): 首先,內(nèi)核可以同時(shí)把同一個(gè)進(jìn)程中的多個(gè)線程調(diào)度到多個(gè)處理器中;其次,進(jìn)程中的一個(gè)線程被阻塞時(shí),內(nèi)核可以調(diào)度同一個(gè)進(jìn)程中的另一個(gè)線程。KLT方法的另一個(gè)優(yōu)點(diǎn)是,內(nèi)核例程自身也可是多線程的。
與ULT方法相比,KLT方法的主要缺點(diǎn)是: 在把控制權(quán)從一個(gè)線程傳送到同一個(gè)進(jìn)程內(nèi)的另一個(gè)線程時(shí),需要切換到內(nèi)核模式。
為說(shuō)明兩種方法的區(qū)別,表4.1給出了在單處理器VAX機(jī)上運(yùn)行類(lèi)UNIX操作系統(tǒng)的測(cè)量結(jié)果。表中進(jìn)行了兩個(gè)基準(zhǔn)測(cè)試,即Null Fork和Signal-Wait,前者測(cè)量創(chuàng)建、調(diào)度、執(zhí)行和完成一個(gè)調(diào)用空過(guò)程的進(jìn)程/線程的時(shí)間(即派生一個(gè)進(jìn)程/線程的開(kāi)銷(xiāo)),后者測(cè)量進(jìn)程/線程給正在等待的進(jìn)程/線程發(fā)信號(hào),然后在某個(gè)條件上等待所需要的時(shí)間(即兩個(gè)進(jìn)程/線程的同步時(shí)間)。可以看出,ULT和KLT之間、KLT和進(jìn)程之間都有一個(gè)數(shù)量級(jí)以上的性能差距。
從表中可以看出,雖然使用KLT多線程技術(shù)與使用單線程的進(jìn)程相比,速度明顯提升,但使用ULT與使用KLT相比,速度再次得以提升。速度的再次提升是否能實(shí)現(xiàn),取決于應(yīng)用程序的性質(zhì)。應(yīng)用程序中的大多數(shù)線程切換都需要內(nèi)核模式訪問(wèn)時(shí),基于ULT的方案不見(jiàn)得會(huì)比基于KLT的方案好。
混合方法
有些操作系統(tǒng)提供混合ULT和KLT的方法【見(jiàn)圖4.5(c)】。在混合系統(tǒng)中,線程創(chuàng)建完全在用戶(hù)空間中完成,線程的調(diào)度和同步也在應(yīng)用程序中進(jìn)行。一個(gè)應(yīng)用程序中的多個(gè)用戶(hù)級(jí)線程會(huì)被映射到一些(小于等于用戶(hù)級(jí)線程數(shù))內(nèi)核級(jí)線程上。為使整體性能最佳,程序員可為特定應(yīng)用程序和處理器調(diào)節(jié)KLT的數(shù)量。
在混合方法中,同一個(gè)應(yīng)用程序中的多個(gè)線程可在多個(gè)處理器上并行地運(yùn)行,某個(gè)會(huì)引起阻塞的系統(tǒng)調(diào)用不會(huì)阻塞整個(gè)進(jìn)程。設(shè)計(jì)正確時(shí),這種方法可結(jié)合純ULT方法和純KLT方法的優(yōu)點(diǎn),并克服它們的缺點(diǎn)。
Solaris操作系統(tǒng)是使用混合方法的很好例子。最新版的Solaris將ULT/KLT關(guān)系限制為1:1。
三 多核和多線程
使用多核系統(tǒng)支持單個(gè)多線程應(yīng)用程序的情況可能會(huì)出現(xiàn)在工作站、游戲機(jī)或正運(yùn)行處理器密集型應(yīng)用的個(gè)人計(jì)算機(jī)上。這時(shí)會(huì)帶來(lái)性能和應(yīng)用程序設(shè)計(jì)上的一些問(wèn)題。本節(jié)首先介紹在多核系統(tǒng)上運(yùn)行的多線程應(yīng)用程序性能,然后介紹采用多核系統(tǒng)性能設(shè)計(jì)應(yīng)用程序的一個(gè)具體示例。
3.1多核系統(tǒng)上的軟件性能
多核組織結(jié)構(gòu)帶來(lái)的性能提升,取決于應(yīng)用程序有效利用并行資源的能力。我們首先介紹運(yùn)行在多核系統(tǒng)上的單個(gè)應(yīng)用程序。Amdahl定律聲稱(chēng)
該定律假設(shè)程序執(zhí)行時(shí)間的(1-f)分之一所涉及的代碼本質(zhì)上是串行的,其余f分之一所涉及的代碼是無(wú)限并行的,并且沒(méi)有調(diào)度開(kāi)銷(xiāo)。
該定律看上去使得多核組織結(jié)構(gòu)的前景很迷人。然而,如圖4.7(a)所示,即使是一小部分串行代碼也會(huì)顯著影響性能。假設(shè)只有10%的代碼本質(zhì)上是串行的(即f=0.9),那么在一個(gè)8處理器的多核系統(tǒng)上運(yùn)行該程序也僅有4.7倍的性能提升。另外,多處理器任務(wù)調(diào)度和通信以及高速緩存一致性維護(hù)都會(huì)給軟件帶來(lái)額外的開(kāi)銷(xiāo)。這就使得性能曲線達(dá)到峰值后便開(kāi)始下降。圖4.7(b)是一個(gè)有代表性的例子。
盡管如此,軟件工程師們一直在努力解決這個(gè)問(wèn)題,而且發(fā)現(xiàn)大量應(yīng)用程序可以有效利用多核系統(tǒng)。研究了一系列數(shù)據(jù)庫(kù)應(yīng)用程序,這些程序采取了很多措施來(lái)降低硬件組織結(jié)構(gòu)、操作系統(tǒng)、中間件和數(shù)據(jù)庫(kù)應(yīng)用軟件本身的串行部分比例。 從圖4.8中可以看出,數(shù)據(jù)庫(kù)管理系統(tǒng)和數(shù)據(jù)庫(kù)應(yīng)用程序能有效地使用多核系統(tǒng)。還有許多不同類(lèi)型的服務(wù)器程序能夠有效使用并行化的多核組織結(jié)構(gòu),因?yàn)榉?wù)器程序通常會(huì)并行地處理許多相對(duì)獨(dú)立的事務(wù)。
除通用服務(wù)器軟件外,其他類(lèi)型的應(yīng)用程序也可從多核系統(tǒng)中直接獲益,因?yàn)樗鼈兊耐掏铝磕茈S著處理器核心的數(shù)量伸縮。【MCDO06】給出了如下示例:
- 原生多線程應(yīng)用程序:多線程應(yīng)用程序的特征是具有少數(shù)幾個(gè)高度線程化的進(jìn)程。線程化應(yīng)用程序的例子包括Lotus Domino和SiebelCRM(客戶(hù)關(guān)系管理)。
- 多進(jìn)程應(yīng)用程序: 多進(jìn)程應(yīng)用程序的特征是具有多個(gè)單線程的進(jìn)程。多進(jìn)程應(yīng)用程序的例子包括Oracle數(shù)據(jù)庫(kù)、SAP和PeopleSoft。
- Java 應(yīng)用程序: Java從根本上支持線程的概念。不僅Java語(yǔ)言本身能夠很方便地支持多線程應(yīng)用程序開(kāi)發(fā),Java虛擬機(jī)也是一個(gè)多線程進(jìn)程,它為Java應(yīng)用程序提供調(diào)度機(jī)制和內(nèi)存管理。能夠直接從多核系統(tǒng)資源中獲益的Java應(yīng)用程序包括Sun公司的Java應(yīng)用服務(wù)器、BEA公司的WebLogic、IBM公司的WebSphere和開(kāi)源的Tomcat應(yīng)用服務(wù)器。基于J2EE開(kāi)發(fā)的所有應(yīng)用程序也可直接從多核技術(shù)中獲益。
- 多實(shí)例應(yīng)用程序: 即使個(gè)別應(yīng)用程序未利用大量的線程來(lái)達(dá)到伸縮性,仍然可以通過(guò)并行運(yùn)行多個(gè)應(yīng)用程序的實(shí)例來(lái)從多核組織結(jié)構(gòu)中獲益。多個(gè)應(yīng)用程序?qū)嵗枰欢ǔ潭壬系母綦x性時(shí),可使用虛擬化技術(shù)(虛擬出支撐操作系統(tǒng)的硬件)為每個(gè)實(shí)例提供獨(dú)立、安全的環(huán)境。
3.2應(yīng)用示例:
Valve 娛樂(lè)科技公司開(kāi)發(fā)了眾多游戲和Source 游戲引擎。Source是玩家數(shù)量最多的游戲引擎之一。Valve公司在自己的游戲中使用Source引擎,并通過(guò)頒發(fā)許可證的方式來(lái)允許其他游戲開(kāi)發(fā)者使用Source引擎。
近年來(lái),Valve使用多線程技術(shù)重新編寫(xiě)了Source引擎,以充分利用Intel和AMD多核處理器芯片的性能。改進(jìn)后的Source引擎代碼為Valve公司的游戲如《半條命2》提供了更為強(qiáng)大的支持。
從Valve的角度來(lái)看,線程根據(jù)其粒度定義為如下幾種:
? 粗粒度線程:分配到各個(gè)處理器上的各個(gè)模塊(稱(chēng)為系統(tǒng))。在Source引擎中,一個(gè)處理器負(fù)責(zé)渲染,一個(gè)處理器負(fù)責(zé)AI(人工智能),一個(gè)處理器負(fù)責(zé)物理計(jì)算,以此類(lèi)推。這種策略非常簡(jiǎn)單。從本質(zhì)上講,每個(gè)主要模塊都是單線程的,由一個(gè)時(shí)間軸線程來(lái)協(xié)調(diào)所有其他線程的同步。
? 細(xì)粒度線程:分布在多個(gè)處理器上的許多相似或相同的任務(wù)。例如,在數(shù)據(jù)數(shù)組上迭代的一個(gè)循環(huán)可分割為一些小循環(huán),而小循環(huán)則在各個(gè)線程上并行執(zhí)行。
? 混合線程:這涉及為某些系統(tǒng)選擇性使用細(xì)粒度線程,或?yàn)槠渌到y(tǒng)使用單個(gè)線程。
Valve公司發(fā)現(xiàn),使用粗粒度線程策略時(shí),在雙處理器上運(yùn)行程序的性能是在單處理器上運(yùn)行程序的性能的兩倍,但這種性能提升僅在某些專(zhuān)門(mén)設(shè)計(jì)的情形下才能達(dá)到。在真實(shí)的游戲環(huán)境中,性能約能提升為原來(lái)的1.2倍。Valve公司還發(fā)現(xiàn)有效地利用細(xì)粒度線程非常困難,因?yàn)槊總€(gè)工作單元耗費(fèi)的時(shí)間是變化的,而且管理輸出和結(jié)果的時(shí)間軸所涉及的編程相當(dāng)復(fù)雜。
Valve公司還發(fā)現(xiàn),隨著8顆核心甚至16顆核心的多核系統(tǒng)的出現(xiàn),混合線程方法最具應(yīng)用前景,因此它可實(shí)現(xiàn)最佳的加速比。Valve識(shí)別了那些只在單處理器上運(yùn)行時(shí)才非常有效率的系統(tǒng)。例如,混音系統(tǒng)幾乎不與用戶(hù)交互,它只處理自身的數(shù)據(jù)集,且不受窗口的幀配置限制。其他模塊(如場(chǎng)景渲染)可組織為許多線程,這類(lèi)模塊既可以在單個(gè)處理器上運(yùn)行,也可以在多個(gè)處理器上運(yùn)行,因此能獲得更好的性能。
根據(jù)系統(tǒng)特性來(lái)選擇使用的單個(gè)線程或多個(gè)線程,可以有效的提高加速比。如果在可以使用多個(gè)線程的系統(tǒng)中使用了單個(gè)線程,性能自然會(huì)有所損失。而在應(yīng)該使用單個(gè)線程的地方使用了多個(gè)線程來(lái)執(zhí)行反而會(huì)使性能下降,也同時(shí)增加了編程的復(fù)雜性。
四 Linux的進(jìn)程和線程管理
4.1 Linux進(jìn)程
Linux中的進(jìn)程或任務(wù)由一個(gè)task struct數(shù)據(jù)結(jié)構(gòu)表示。task struct數(shù)據(jù)結(jié)構(gòu)包含以下各類(lèi)信息:
- 狀態(tài):進(jìn)程的執(zhí)行狀態(tài)(執(zhí)行態(tài)、就緒態(tài)、掛起態(tài)、停止態(tài)、僵死態(tài))。這些狀態(tài)將在下面進(jìn)一步講述。
- 調(diào)度信息:Linux調(diào)度進(jìn)程所需要的信息。一個(gè)進(jìn)程可能是普通的或?qū)崟r(shí)的,并具有優(yōu)先級(jí)。
- 實(shí)時(shí)進(jìn)程在普通進(jìn)程前調(diào)度,且在每類(lèi)中使用相關(guān)的優(yōu)先級(jí)。一個(gè)計(jì)數(shù)器會(huì)記錄允許進(jìn)程執(zhí)行的時(shí)間量。
- 標(biāo)識(shí)符:每個(gè)進(jìn)程都有唯一的一個(gè)進(jìn)程標(biāo)識(shí)符,以及用戶(hù)標(biāo)識(shí)符和組標(biāo)識(shí)符。組標(biāo)識(shí)符用于給一組進(jìn)程指定資源訪問(wèn)特權(quán)。
- 進(jìn)程間通信:Linux支持UNIXSVR4中的IPC機(jī)制,詳見(jiàn)第6章。
- 鏈接:每個(gè)進(jìn)程都有一個(gè)到其父進(jìn)程的鏈接及到其兄弟進(jìn)程(與它有相同的父進(jìn)程)的鏈接,以及到所有子進(jìn)程的鏈接。
- 時(shí)間和計(jì)時(shí)器:包括進(jìn)程創(chuàng)建的時(shí)刻和進(jìn)程所消耗的處理器時(shí)間總量。一個(gè)進(jìn)程可能還有一個(gè)或多個(gè)間隔計(jì)時(shí)器,進(jìn)程通過(guò)系統(tǒng)調(diào)用來(lái)定義間隔計(jì)時(shí)器,計(jì)時(shí)器期滿(mǎn)時(shí),會(huì)給進(jìn)程發(fā)送一個(gè)信號(hào)。計(jì)時(shí)器可以只用一次或周期性地使用。
- 文件系統(tǒng):包括指向被該進(jìn)程打開(kāi)的任何文件的指針和指向該進(jìn)程當(dāng)前目錄與根目錄的指針。
- 地址空間:定義分配給該進(jìn)程的虛擬地址空間。
- 處理器專(zhuān)用上下文:構(gòu)成該進(jìn)程上下文的寄存器和棧信息。
圖4.15給出了Linux中一個(gè)進(jìn)程的執(zhí)行狀態(tài),如下所示:
- 運(yùn)行:這個(gè)狀態(tài)值對(duì)應(yīng)于兩個(gè)狀態(tài)。一個(gè)運(yùn)行進(jìn)程要么正在執(zhí)行,要么準(zhǔn)備執(zhí)行。
- 可中斷:這是一個(gè)阻塞態(tài),此時(shí)進(jìn)程正在等待一個(gè)事件(如一個(gè)I/O操作)的結(jié)束、一個(gè)可用的資源或另一個(gè)進(jìn)程的信號(hào)。
- 不可中斷:這是另一個(gè)阻塞態(tài)。它與可中斷狀態(tài)的區(qū)別是,在不可中斷狀態(tài)下,進(jìn)程正在等待一個(gè)硬件條件,因此不會(huì)接收任何信號(hào)。
- 停止:進(jìn)程被中止,并且只能由來(lái)自另一個(gè)進(jìn)程的主動(dòng)動(dòng)作恢復(fù)。例如,一個(gè)正在被調(diào)試的進(jìn)程可被置于停止態(tài)。
- 僵死:進(jìn)程已被終止,但由于某些原因,在進(jìn)程表中仍然有其任務(wù)結(jié)構(gòu)。
4.2 Linux線程
傳統(tǒng)UNIX系統(tǒng)支持在每個(gè)進(jìn)程中只執(zhí)行一個(gè)線程,但現(xiàn)代UNIX系統(tǒng)通常支持在一個(gè)進(jìn)程中含有多個(gè)內(nèi)核級(jí)線程。如同傳統(tǒng)UNIX系統(tǒng)那樣,老版本的Linux內(nèi)核不支持多線程。多線程應(yīng)用程序需要用一組用戶(hù)級(jí)程序庫(kù)來(lái)編寫(xiě),以便將所有線程映射到一個(gè)單獨(dú)的內(nèi)核級(jí)進(jìn)程中,最著名的是pthread(POSIX thread)庫(kù)。現(xiàn)代UNIX提供內(nèi)核級(jí)線程。Linux提供一種不區(qū)分進(jìn)程和線程的解決方案,這種解決方案使用一種類(lèi)似于Solaris輕量級(jí)進(jìn)程的方法,將用戶(hù)級(jí)線程映射到內(nèi)核級(jí)進(jìn)程。組成一個(gè)用戶(hù)級(jí)進(jìn)程的多個(gè)用戶(hù)級(jí)線程則映射到共享同一個(gè)組ID的多個(gè)Linux內(nèi)核級(jí)進(jìn)程上。因此,這些進(jìn)程可以共享文件和內(nèi)存等資源,使得同一個(gè)組中的進(jìn)程調(diào)度切換時(shí)不需要切換上下文。
在Linux中通過(guò)復(fù)制當(dāng)前進(jìn)程的屬性可創(chuàng)建一個(gè)新進(jìn)程。新進(jìn)程創(chuàng)建后,可以共享資源,如文件、信號(hào)處理程序和虛存。兩個(gè)進(jìn)程共享相同的虛存時(shí),可將它們視為一個(gè)進(jìn)程中的線程。 然而,沒(méi)有給線程單獨(dú)定義數(shù)據(jù)結(jié)構(gòu),因此Linux中的進(jìn)程和線程沒(méi)有區(qū)別。Linux用clone()命令代替通常的fork()命令來(lái)創(chuàng)建進(jìn)程。該命令包含了下面所示的一組標(biāo)識(shí)作為參數(shù)。傳統(tǒng)的fork()系統(tǒng)調(diào)用在Linux上是用所有克隆標(biāo)志清零的clone()系統(tǒng)調(diào)用實(shí)現(xiàn)的。
克隆標(biāo)識(shí)包括以下實(shí)例:
- CLONE_NEWPID:創(chuàng)造新的進(jìn)程ID命名空間。
- CLONE PARENT:調(diào)用者及其創(chuàng)建的任務(wù)共享同一個(gè)父進(jìn)程。
- CLONE_SYSVSEM:共享System V SEM_UNDO語(yǔ)義。
- CLONE_THREAD:將該進(jìn)程插入父進(jìn)程的同一個(gè)線程組。該標(biāo)志為真時(shí),它隱含設(shè)置了
CLONE_PARENT。 - CLONE_VM:共享地址空間(內(nèi)存描述符和所有頁(yè)表)。
當(dāng)Linux內(nèi)核執(zhí)行從一個(gè)進(jìn)程到另一個(gè)進(jìn)程的切換時(shí),會(huì)檢查當(dāng)前進(jìn)程的頁(yè)目錄地址是否與將被調(diào)度的進(jìn)程的相同。若相同,則它們共享同一個(gè)地址空間,所以此時(shí)上下文切換僅是從代碼的一處跳轉(zhuǎn)到代碼的另一處。
雖然屬于同一個(gè)進(jìn)程組的克隆進(jìn)程共享同一內(nèi)存空間,但不能共享同一個(gè)用戶(hù)棧。所以clone()調(diào)用會(huì)為每個(gè)進(jìn)程創(chuàng)建獨(dú)立的棧空間。
五 小結(jié)
某些操作系統(tǒng)區(qū)分進(jìn)程和線程的概念,前者涉及資源的所有權(quán),后者涉及程序的執(zhí)行。這種方法可提高性能,方便編碼。在多線程系統(tǒng)中,可在一個(gè)進(jìn)程內(nèi)定義多個(gè)并發(fā)線程,實(shí)現(xiàn)方法是使用用戶(hù)級(jí)線程或內(nèi)核級(jí)線程。用戶(hù)級(jí)線程對(duì)操作系統(tǒng)是未知的,它們由一個(gè)在進(jìn)程的用戶(hù)空間中運(yùn)行的線程庫(kù)創(chuàng)建并管理。用戶(hù)級(jí)線程非常高效,因?yàn)閺囊粋€(gè)線程切換到另一個(gè)線程不需要進(jìn)行狀態(tài)切換,但一個(gè)進(jìn)程中一次只有一個(gè)用戶(hù)級(jí)線程可以執(zhí)行,如果一個(gè)線程發(fā)生阻塞,整個(gè)進(jìn)程都會(huì)被阻塞。進(jìn)程內(nèi)包含的內(nèi)核級(jí)線程是由內(nèi)核維護(hù)的。由于內(nèi)核認(rèn)識(shí)它們,因而同一個(gè)進(jìn)程中的多個(gè)線程可在多個(gè)處理器上并行執(zhí)行,一個(gè)線程的阻塞不會(huì)阻塞整個(gè)進(jìn)程,但從一個(gè)線程切換到另一個(gè)線程時(shí)需要進(jìn)行模式轉(zhuǎn)換。
總結(jié)
- 上一篇: 操作系统-进程
- 下一篇: 操作系统-并发性:互斥与同步