第二章 進程的描述與控制
2.1 前趨圖和程序執行
在早期未配置OS的系統和單道批處理系統中,程序的執行方式是順序執行,即在內存中僅裝入一道用戶程序,由它獨占系統中的所有資源,只有在一個用戶程序執行完成后,才允許裝入另一個程序並執行。可見,這種方式浪費資源、系統運行效率低等缺點。
2.1.1 前趨圖
為了能更好地描述程序的順序和並發執行情況,我們先介紹用於描述程序執行先后順序的前趨圖。所謂前趨圖(Precedence Graph),是指一個有向無循環圖,可記為DAG(Directed Acyclic Graph),它用於描述進程之間執行的先后順序。圖中的每個結點可用來表示一個進程或程序段,乃至一條語句,結點間的有向邊則表示兩個結點之間存在的偏序(Partial Order)或前趨關系(Precedence Relation)。
進程(或程序)之間的前趨關系可用“→”來表示,如果進程Pi和Pj存在着前趨關系,可表示為(Pi,Pj)∈→,也可寫成Pi→Pj,表示在Pj開始執行之前Pi 必須完成。此時稱Pi是Pj的直接前趨,而稱Pj是Pi的直接后繼。在前趨圖中,把沒有前趨的結點稱為初始結點(Initial Node),把沒有后繼的結點稱為終止結點(Final Node)。此外,每個結點還具有一個重量(Weight),用於表示該結點所含有的程序量或程序的執行時間。
在圖2-1(a)所示的前趨圖中,存在着如下前趨關系: P1→P2,P1→P3,P1→P4,P2→P5,P3→P5,P4→P6,P4→P7,P5→P8,P6→P8,P7→P9,P8→P9或表示為: P={P1, P2, P3, P4, P5, P6, P7, P8, P9} ={(P1, P2), (P1, P3), (P1, P4), (P2, P5), (P3, P5), (P4, P6), (P4, P7), (P5, P8), (P6, P8), (P7, P9), (P8, P9)}
應當注意,前趨圖中是不允許有循環的,否則必然會產生不可能實現的前趨關系。如圖2-1(b)所示的前趨關系中就存在着循環。它一方面要求在S3開始執行之前,S2必須完成,另一方面又要求在S2開始執行之前,S3必須完成。顯然,這種關系是不可能實現的。
S2→S3,S3→S2

圖2-1 前趨圖
2.1.2 程序順序執行
1. 程序的順序執行
通常,一個應用程序由若干個程序段組成,每一個程序段完成特定的功能,它們在執行時,都需要按照某種先后次序順序執行,僅當前一程序段執行完后,才運行后一程序段。例如,在進行計算時,應先運行輸入程序,用於輸入用戶的程序和數據;然后運行計算程序,對所輸入的數據進行計算;最后才是運行打印程序,打印計算結果。我們用結點(Node)代表各程序段的操作(在圖2-1中用圓圈表示),其中I代表輸入操作,C代表計算操作,P為打印操作,用箭頭指示操作的先后次序。
這樣,上述的三個程序段間就存在着這樣的前趨關系:Ii→Ci→Pi,其執行的順序可用前趨圖2-2(a)描述。 即使是一個程序段,也可能存在着執行順序問題,下面示出了一個包含了三條語句的程序段:
S1: a :=x+y; S2: b :=a-5; S3: c :=b+1;
其中,語句S2必須在語句S1后(即a被賦值)才能執行,語句S3也只能在b被賦值后才能執行,因此,三條語句存在着這樣的前趨關系:S1→S2→S3,應按前趨圖2-2(b)所示的順序執行。

圖2-2 程序順序執行的前趨圖
2. 程序順序執行時的特征
由上所述可以得知,在程序順序執行時,具有這樣三個特征:
① 順序性:指處理機嚴格地按照程序所規定的順序執行,即每一操作必須在下一個操作開始之前結束;
② 封閉性:指程序在封閉的環境下運行,即程序運行時獨占全機資源,資源的狀態(除初始狀態外)只有本程序才能改變它,程序一旦開始執行,其執行結果不受外界因素影響;
③ 可再現性:指只要程序執行時的環境和初始條件相同,當程序重復執行時,不論它是從頭到尾不停頓地執行,還是“停停走走”地執行,都可獲得相同的結果。程序順序執行時的這種特性,為程序員檢測和校正程序的錯誤帶來了很大的方便。
2.1.3 程序並發執行
1. 程序的並發執行
我們通過一個常見的例子來說明程序的順序執行和並發執行。在圖2-2中的輸入程序、計算程序和打印程序三者之間,存在着Ii→Ci→Pi這樣的前趨關系,以至對一個作業的輸入、計算和打印三個程序段必須順序執行。但若是對一批作業進行處理時,每道作業的輸入、計算和打印程序段的執行情況如圖2-3所示。

圖2-3 程序並發執行時的前趨圖
由圖2-3可以看出,存在前趨關系Ii→Ci,Ii→Ii+1,Ci→Pi,Ci→Ci+1,Pi→Pi+1,而Ii+1和Ci及Pi-1是重疊的,即在Pi-1和Ci以及Ii+1之間,不存在前趨關系,可以並發執行。
對於具有下述四條語句的程序段:
S1: a :=x+2 S2: b :=y+4 S3: c :=a+b S4: d :=c+b
可畫出圖2-4所示的前趨關系。可以看出:S3必須在a和b被賦值后方能執行;S4必須在S3之后執行;但S1和S2則可以並發執行,因為它們彼此互不依賴。

圖2-4 四條語句的前趨關系
2. 程序並發執行時的特征
在引入了程序間的並發執行功能后,雖然提高了系統的吞吐量和資源利用率,但由於它們共享系統資源,以及它們為完成同一項任務而相互合作,致使在這些並發執行的程序之間必將形成相互制約的關系,由此會給程序並發執行帶來新的特征。
(1) 間斷性。 (2) 失去封閉性。 (3) 不可再現性。
2.2 進 程 的 描 述
2.2.1 進程的定義和特征
1. 進程的定義
在多道程序環境下,程序的執行屬於並發執行,此時它們將失去其封閉性,並具有間斷性,以及其運行結果不可再現性的特征。由此,決定了通常的程序是不能參與並發執行的,否則,程序的運行也就失去了意義。為了能使程序並發執行,並且可以對並發執行的程序加以描述和控制,人們引入了“進程”的概念。
對於進程的定義,從不同的角度可以有不同的定義,其中較典型的定義有:
(1) 進程是程序的一次執行。
(2) 進程是一個程序及其數據在處理機上順序執行時所發生的活動。
(3) 進程是具有獨立功能的程序在一個數據集合上運行的過程,它是系統進行資源分配和調度的一個獨立單位。
2. 進程的特征
進程和程序是兩個截然不同的概念,除了進程具有程序所沒有的PCB結構外,還具有下面一些特征:
(1) 動態性。 (2) 並發性。 (3) 獨立性。 (4) 異步性。
2.2.2 進程的基本狀態及轉換
1. 進程的三種基本狀態
由於多個進程在並發執行時共享系統資源,致使它們在運行過程中呈現間斷性的運行規律,所以進程在其生命周期內可能具有多種狀態。一般而言,每一個進程至少應處於以下三種基本狀態之一:
(1) 就緒(Ready)狀態。 (2) 執行(Running)狀態。 (3) 阻塞(Block)狀態。
2. 三種基本狀態的轉換
進程在運行過程中會經常發生狀態的轉換。例如,處於就緒狀態的進程,在調度程序為之分配了處理機之后便可執行,相應地,其狀態就由就緒態轉變為執行態;正在執行的進程(當前進程)如果因分配給它的時間片已完而被剝奪處理機暫停執行時,其狀態便由執行轉為就緒;如果因發生某事件,致使當前進程的執行受阻(例如進程訪問某臨界資源,而該資源正被其它進程訪問時),使之無法繼續執行,則該進程狀態將由執行轉變為阻塞。圖2-5示出了進程的三種基本狀態,以及各狀態之間的轉換關系。

圖2-5 進程的三種基本狀態及其轉換
3. 創建狀態和終止狀態
1) 創建狀態
如前所述,進程是由創建而產生。創建一個進程是個很復雜的過程,一般要通過多個步驟才能完成:如首先由進程申請一個空白PCB,並向PCB中填寫用於控制和管理進程的信息;然后為該進程分配運行時所必須的資源;最后,把該進程轉入就緒狀態並插入就緒隊列之中。但如果進程所需的資源尚不能得到滿足,比如系統尚無足夠的內存使進程無法裝入其中,此時創建工作尚未完成,進程不能被調度運行,於是把此時進程所處的狀態稱為創建狀態。
2) 終止狀態
進程的終止也要通過兩個步驟:首先,是等待操作系統進行善后處理,最后將其PCB清零,並將PCB空間返還系統。當一個進程到達了自然結束點,或是出現了無法克服的錯誤,或是被操作系統所終結,或是被其他有終止權的進程所終結,它將進入終止狀態。進入終止態的進程以后不能再執行,但在操作系統中依然保留一個記錄,其中保存狀態碼和一些計時統計數據,供其他進程收集。一旦其他進程完成了對其信息的提取之后,操作系統將刪除該進程,即將其PCB清零,並將該空白PCB返還系統。圖2-6示出了增加了創建狀態和終止狀態后進程的五種狀態及轉換關系圖。

圖2-6 進程的五種基本狀態及轉換
2.2.3 掛起操作和進程狀態的轉換
1. 掛起操作的引入
引入掛起操作的原因,是基於系統和用戶的如下需要:
(1) 終端用戶的需要。 (2) 父進程請求。 (3) 負荷調節的需要。 (4) 操作系統的需要。
2. 引入掛起原語操作后三個進程狀態的轉換
在引入掛起原語Suspend和激活原語Active后,在它們的作用下,進程將可能發生以下幾種狀態的轉換:
(1) 活動就緒→靜止就緒。
(2) 活動阻塞→靜止阻塞。
(3) 靜止就緒→活動就緒。
(4) 靜止阻塞→活動阻塞。
3. 引入掛起操作后五個進程狀態的轉換
如圖2-8示出了增加了創建狀態和終止狀態后具有掛起狀態的進程狀態及轉換圖。
如圖2-8所示,引進創建和終止狀態后,在進程狀態轉換時,與圖2-7所示的進程五狀態轉換相比較,要增加考慮下面的幾種情況:
(1) NULL→創建: (2) 創建→活動就緒: (3) 創建→靜止就緒: (4) 執行→終止:

圖2-7 具有掛起狀態的進程狀態圖

(參考操作系統之進程的狀態和轉換詳解重新繪制)
圖2-8 具有創建、終止和掛起狀態的進程狀態圖
2.2.4 進程管理中的數據結構
1. 操作系統中用於管理控制的數據結構
在計算機系統中,對於每個資源和每個進程都設置了一個數據結構,用於表征其實體,我們稱之為資源信息表或進程信息表,其中包含了資源或進程的標識、描述、狀態等信息以及一批指針。通過這些指針,可以將同類資源或進程的信息表,或者同一進程所占用的資源信息表分類鏈接成不同的隊列,便於操作系統進行查找。
如圖2-9所示,OS管理的這些數據結構一般分為以下四類:內存表、設備表、文件表和用於進程管理的進程表,通常進程表又被稱為進程控制塊PCB。

2. 進程控制塊PCB的作用
(1) 作為獨立運行基本單位的標志。
(2) 能實現間斷性運行方式。
(3) 提供進程管理所需要的信息。
(4) 提供進程調度所需要的信息。
(5) 實現與其它進程的同步與通信。
3. 進程控制塊中的信息
在進程控制塊中,主要包括下述四個方面的信息。
1) 進程標識符
進程標識符用於唯一地標識一個進程。一個進程通常有兩種標識符:
(1) 外部標識符。 (2) 內部標識符。
2) 處理機狀態
處理機狀態信息也稱為處理機的上下文,主要是由處理機的各種寄存器中的內容組成的。
3) 進程調度信息
在OS進行調度時,必須了解進程的狀態及有關進程調度的信息,這些信息包括:
① 進程狀態,指明進程的當前狀態,它是作為進程調度和對換時的依據;
② 進程優先級,是用於描述進程使用處理機的優先級別的一個整數,優先級高的進程應優先獲得處理機;
③ 進程調度所需的其它信息,它們與所采用的進程調度算法有關,比如,進程已等待CPU的時間總和、進程已執行的時間總和等;
④ 事件,是指進程由執行狀態轉變為阻塞狀態所等待發生的事件,即阻塞原因。
4) 進程控制信息
是指用於進程控制所必須的信息,它包括:① 程序和數據的地址,進程實體中的程序和數據的內存或外存地(首)址,以便再調度到該進程執行時,能從PCB中找到其程序和數據;② 進程同步和通信機制,這是實現進程同步和進程通信時必需的機制,如消息隊列指針、信號量等,它們可能全部或部分地放在PCB中;③ 資源清單,在該清單中列出了進程在運行期間所需的全部資源(除CPU以外),另外還有一張已分配到該進程的資源的清單;④ 鏈接指針,它給出了本進程(PCB)所在隊列中的下一個進程的PCB的首地址。
4. 進程控制塊的組織方式
在一個系統中,通常可擁有數十個、數百個乃至數千個PCB。為了能對它們加以有效的管理,應該用適當的方式將這些PCB組織起來。目前常用的組織方式有以下三種。
(1) 線性方式,即將系統中所有的PCB都組織在一張線性表中,將該表的首址存放在內存的一個專用區域中。該方式實現簡單、開銷小,但每次查找時都需要掃描整張表,因此適合進程數目不多的系統。圖2-10示出了線性表的PCB組織方式。

圖2-10 PCB線性表示意圖
(2) 鏈接方式,即把具有相同狀態進程的PCB分別通過PCB中的鏈接字鏈接成一個隊列。這樣,可以形成就緒隊列、若干個阻塞隊列和空白隊列等。對就緒隊列而言,往往按進程的優先級將PCB從高到低進行排列,將優先級高的進程PCB排在隊列的前面。同樣,也可把處於阻塞狀態進程的PCB根據其阻塞原因的不同,排成多個阻塞隊列,如等待I/O操作完成的隊列和等待分配內存的隊列等。圖2-11示出了一種鏈接隊列的組織方式。

圖2-11 PCB鏈接隊列示意圖
(3) 索引方式,即系統根據所有進程狀態的不同,建立幾張索引表,例如,就緒索引表、阻塞索引表等,並把各索引表在內存的首地址記錄在內存的一些專用單元中。在每個索引表的表目中,記錄具有相應狀態的某個PCB在PCB表中的地址。圖2-12示出了索引方式的PCB組織。

圖2-12 按索引方式組織PCB
2.3 進 程 控 制
進程控制是進程管理中最基本的功能,主要包括創建新進程、終止已完成的進程、將因發生異常情況而無法繼續運行的進程置於阻塞狀態、負責進程運行中的狀態轉換等功能。如當一個正在執行的進程因等待某事件而暫時不能繼續執行時,將其轉變為阻塞狀態,而在該進程所期待的事件出現后,又將該進程轉換為就緒狀態等。進程控制一般是由OS的內核中的原語來實現的。
2.3.1 操作系統內核
1. 支撐功能 (1) 中斷處理。 (2) 時鍾管理。 (3) 原語操作。
2. 資源管理功能 (1) 進程管理。 (2) 存儲器管理。 (3) 設備管理。
2.3.2 進程的創建
1. 進程的層次結構
在OS中,允許一個進程創建另一個進程,通常把創建進程的進程稱為父進程,而把被創建的進程稱為子進程。子進程可繼續創建更多的孫進程,由此便形成了一個進程的層次結構。如在UNIX中,進程與其子孫進程共同組成一個進程家族(組)。
2. 進程圖
為了形象地描述一個進程的家族關系而引入了進程圖(Process Graph)。所謂進程圖就是用於描述進程間關系的一棵有向樹,如圖2-13所示。

圖2-13 進程樹
3. 引起創建進程的事件
為使程序之間能並發運行,應先為它們分別創建進程。導致一個進程去創建另一個進程的典型事件有四類:
(1) 用戶登錄。 (2) 作業調度。 (3) 提供服務。 (4) 應用請求。
4. 進程的創建(Creation of Process)
在系統中每當出現了創建新進程的請求后,OS便調用進程創建原語Creat按下述步驟創建一個新進程:
(1) 申請空白PCB,為新進程申請獲得唯一的數字標識符,並從PCB集合中索取一個空白PCB。
(2) 為新進程分配其運行所需的資源,包括各種物理和邏輯資源,如內存、文件、I/O設備和CPU時間等。
(3) 初始化進程控制塊(PCB)。
(4) 如果進程就緒隊列能夠接納新進程,便將新進程插入就緒隊列。
2.3.3 進程的終止
1. 引起進程終止(Termination of Process)的事件
(1) 正常結束 (2) 異常結束 (3) 外界干預
2. 進程的終止過程
如果系統中發生了要求終止進程的某事件,OS便調用進程終止原語,按下述過程去終止指定的進程:
(1) 根據被終止進程的標識符,從PCB集合中檢索出該進程的PCB,從中讀出該進程的狀態;
(2) 若被終止進程正處於執行狀態,應立即終止該進程的執行,並置調度標志為真,用於指示該進程被終止后應重新進行調度;
(3) 若該進程還有子孫進程,還應將其所有子孫進程也都予以終止,以防它們成為不可控的進程;
(4) 將被終止進程所擁有的全部資源或者歸還給其父進程,或者歸還給系統;
(5) 將被終止進程(PCB)從所在隊列(或鏈表)中移出,等待其它程序來搜集信息。
2.3.4 進程的阻塞與喚醒
1. 引起進程阻塞和喚醒的事件
有下述幾類事件會引起進程阻塞或被喚醒:
(1) 向系統請求共享資源失敗。 (2) 等待某種操作的完成。 (3) 新數據尚未到達。 (4) 等待新任務的到達。
2. 進程阻塞過程
正在執行的進程,如果發生了上述某事件,進程便通過調用阻塞原語block將自己阻塞。可見,阻塞是進程自身的一種主動行為。進入block過程后,由於該進程還處於執行狀態,所以應先立即停止執行,把進程控制塊中的現行狀態由“執行”改為阻塞,並將PCB插入阻塞隊列。如果系統中設置了因不同事件而阻塞的多個阻塞隊列,則應將本進程插入到具有相同事件的阻塞隊列。最后,轉調度程序進行重新調度,將處理機分配給另一就緒進程,並進行切換,亦即,保留被阻塞進程的處理機狀態,按新進程的PCB中的處理機狀態設置CPU的環境。
3. 進程喚醒過程
當被阻塞進程所期待的事件發生時,比如它所啟動的I/O操作已完成,或其所期待的數據已經到達,則由有關進程(比如提供數據的進程)調用喚醒原語wakeup,將等待該事件的進程喚醒。wakeup執行的過程是:首先把被阻塞的進程從等待該事件的阻塞隊列中移出,將其PCB中的現行狀態由阻塞改為就緒,然后再將該PCB插入到就緒隊列中。
2.3.5 進程的掛起與激活
1. 進程的掛起 2. 進程的激活過程
2.4 進 程 同 步
在OS中引入進程后,一方面可以使系統中的多道程序並發執行,這不僅能有效地改善資源利用率,還可顯著地提高系統的吞吐量,但另一方面卻使系統變得更加復雜。如果不能采取有效的措施,對多個進程的運行進行妥善的管理,必然會因為這些進程對系統資源的無序爭奪給系統造成混亂。致使每次處理的結果存在着不確定性,即顯現出其不可再現性。
2.4.1 進程同步的基本概念
1. 兩種形式的制約關系
1) 間接相互制約關系
2) 直接相互制約關系
2. 臨界資源(Critical Resouce)
在第一章中我們曾經介紹過,許多硬件資源如打印機、 磁帶機等,都屬於臨界資源,諸進程間應采取互斥方式,實現對這種資源的共享。
3. 臨界區(critical section)
由前所述可知,不論是硬件臨界資源還是軟件臨界資源,多個進程必須互斥地對它進行訪問。人們把在每個進程中訪問臨界資源的那段代碼稱為臨界區(critical section)。
4. 同步機制應遵循的規則
為實現進程互斥地進入自己的臨界區,可用軟件方法,更多的是在系統中設置專門的同步機構來協調各進程間的運行。所有同步機制都應遵循下述四條准則:
(1) 空閑讓進。 (2) 忙則等待。 (3) 有限等待。 (4) 讓權等待。
2.4.2 硬件同步機制
雖然可以利用軟件方法解決諸進程互斥進入臨界區的問題,但有一定難度,並且存在很大的局限性,因而現在已很少采用。相應地,目前許多計算機已提供了一些特殊的硬件指令,允許對一個字中的內容進行檢測和修正,或者是對兩個字的內容進行交換等。可利用這些特殊的指令來解決臨界區問題。
1. 關中斷
關中斷是實現互斥的最簡單的方法之一。在進入鎖測試之前關閉中斷,直到完成鎖測試並上鎖之后才能打開中斷。這樣,進程在臨界區執行期間,計算機系統不響應中斷,從而不會引發調度,也就不會發生進程或線程切換。由此,保證了對鎖的測試和關鎖操作的連續性和完整性,有效地保證了互斥。但是,關中斷的方法存在許多缺點:① 濫用關中斷權力可能導致嚴重后果;② 關中斷時間過長,會影響系統效率,限制了處理器交叉執行程序的能力;③ 關中斷方法也不適用於多CPU 系統,因為在一個處理器上關中斷並不能防止進程在其它處理器上執行相同的臨界段代碼。
2. 利用Test-and-Set指令實現互斥
這是一種借助一條硬件指令——“測試並建立”指令TS(Test-and-Set)以實現互斥的方法。在許多計算機中都提供了這種指令。
3. 利用Swap指令實現進程互斥
該指令稱為對換指令,在Intel 80x86中又稱為XCHG指令,用於交換兩個字的內容。
2.4.3 信號量機制
1. 整型信號量
最初由Dijkstra把整型信號量定義為一個用於表示資源數目的整型量S,它與一般整型量不同,除初始化外,僅能通過兩個標准的原子操作(Atomic Operation) wait(S)和signal(S)來訪問。很長時間以來,這兩個操作一直被分別稱為P、V操作。
2. 記錄型信號量
在整型信號量機制中的wait操作,只要是信號量S≤0,就會不斷地測試。因此,該機制並未遵循“讓權等待”的准則,而是使進程處於“忙等”的狀態。記錄型信號量機制則是一種不存在“忙等”現象的進程同步機制。但在采取了“讓權等待”的策略后,又會出現多個進程等待訪問同一臨界資源的情況。為此,在信號量機制中,除了需要一個用於代表資源數目的整型變量value外,還應增加一個進程鏈表指針list,用於鏈接上述的所有等待進程。
3. AND型信號量
前面所述的進程互斥問題針對的是多個並發進程僅共享一個臨界資源的情況。在有些應用場合,是一個進程往往需要獲得兩個或更多的共享資源后方能執行其任務。假定現有兩個進程A和B,它們都要求訪問共享數據D和E,當然,共享數據都應作為臨界資源。
4. 信號量集
在前面所述的記錄型信號量機制中,wait(S)或signal(S)操作僅能對信號量施以加1或減1操作,意味着每次只能對某類臨界資源進行一個單位的申請或釋放。當一次需要N個單位時,便要進行N次wait(S)操作,這顯然是低效的,甚至會增加死鎖的概率。此外,在有些情況下,為確保系統的安全性,當所申請的資源數量低於某一下限值時,還必須進行管制,不予以分配。因此,當進程申請某類臨界資源時,在每次分配之前,都必須測試資源的數量,判斷是否大於可分配的下限值,決定是否予以分配。
2.4.4 信號量的應用
1. 利用信號量實現進程互斥
為使多個進程能互斥地訪問某臨界資源,只需為該資源設置一互斥信號量mutex,並設其初始值為1,然后將各進程訪問該資源的臨界區CS置於wait(mutex)和signal(mutex)操作之間即可。
2. 利用信號量實現前趨關系
還可利用信號量來描述程序或語句之間的前趨關系。設有兩個並發執行的進程P1和P2。P1中有語句S1;P2中有語句S2。我們希望在S1執行后再執行S2。為實現這種前趨關系,只需使進程P1和P2共享一個公用信號量S,並賦予其初值為0,將signal(S)操作放在語句S1后面,而在S2語句前面插入wait(S)操作,即
在進程P1中,用S1;signal(S);
在進程P2中,用wait(S);S2;
由於S被初始化為0,這樣,若P2先執行必定阻塞,只有在進程P1執行完S1; signal(S);操作后使S增為1時,P2進程方能成功執行語句S2。同樣,我們可以利用信號量按照語句間的前趨關系(見圖2-14),寫出一個更為復雜的可並發執行的程序。

圖2-14 前趨圖舉例
2.4.5 管程機制
1.管程的定義
系統中的各種硬件資源和軟件資源均可用數據結構抽象地描述其資源特性,即用少量信息和對該資源所執行的操作來表征該資源,而忽略它們的內部結構和實現細節。
由上述的定義可知,管程由四部分組成:① 管程的名稱;② 局部於管程的共享數據結構說明;③ 對該數據結構進行操作的一組過程;④ 對局部於管程的共享數據設置初始值的語句。圖2-15是一個管程的示意圖。

圖2-15 管程的示意圖
2. 條件變量
在利用管程實現進程同步時,必須設置同步工具,如兩個同步操作原語wait和signal。當某進程通過管程請求獲得臨界資源而未能滿足時,管程便調用wait原語使該進程等待,並將其排在等待隊列上,如圖2-13所示。僅當另一進程訪問完成並釋放該資源之后,管程才又調用signal原語,喚醒等待隊列中的隊首進程。
2.5 經典進程的同步問題
在多道程序環境下,進程同步問題十分重要,也是相當有趣的問題,因而吸引了不少學者對它進行研究,由此而產生了一系列經典的進程同步問題,其中較有代表性的是“生產者—消費者”問題、“讀者—寫者問題”、“哲學家進餐問題”等等。通過對這些問題的研究和學習,可以幫助我們更好地理解進程同步的概念及實現方法。
2.5.1 生產者-消費者問題
1. 利用記錄型信號量解決生產者-消費者問題
假定在生產者和消費者之間的公用緩沖池中具有n個緩沖區,這時可利用互斥信號量mutex實現諸進程對緩沖池的互斥使用;利用信號量empty和full分別表示緩沖池中空緩沖區和滿緩沖區的數量。又假定這些生產者和消費者相互等效,只要緩沖池未滿,生產者便可將消息送入緩沖池;只要緩沖池未空,消費者便可從緩沖池中取走一個消息。
2. 利用AND信號量解決生產者-消費者問題
對於生產者-消費者問題,也可利用AND信號量來解決,即用Swait(empty,mutex)來代替wait(empty)和wait(mutex);用Ssignal(mutex,full)來代替signal(mutex)和signal(full);用Swait(full,mutex)代替wait(full)和wait(mutex),以及用Ssignal(mutex,empty)代替Signal(mutex)和Signal(empty)。
3. 利用管程解決生產者-消費者問題
在利用管程方法來解決生產者-消費者問題時,首先便是為它們建立一個管程,並命名為procducerconsumer,或簡稱為PC。其中包括兩個過程:
(1) put(x)過程。 (2) get(x)過程。
對於條件變量notfull和notempty,分別有兩個過程cwait和csignal對它們進行操作:
(1) cwait(condition)過程:當管程被一個進程占用時,其他進程調用該過程時阻塞,並掛在條件condition的隊列上。
(2) csignal(condition)過程:喚醒在cwait執行后阻塞在條件condition隊列上的進程,如果這樣的進程不止一個,則選擇其中一個實施喚醒操作;如果隊列為空,則無操作而返回。
2.5.2 哲學家進餐問題
1. 利用記錄型信號量解決哲學家進餐問題
經分析可知,放在桌子上的筷子是臨界資源,在一段時間內只允許一位哲學家使用。為了實現對筷子的互斥使用,可以用一個信號量表示一只筷子,由這五個信號量構成信號量數組。
2. 利用AND信號量機制解決哲學家進餐問題
在哲學家進餐問題中,要求每個哲學家先獲得兩個臨界資源(筷子)后方能進餐,這在本質上就是前面所介紹的AND同步問題,故用AND信號量機制可獲得最簡潔的解法。
2.5.3 讀者-寫者問題
1. 利用記錄型信號量解決讀者-寫者問題
為實現Reader與Writer進程間在讀或寫時的互斥而設置了一個互斥信號量Wmutex。另外,再設置一個整型變量Readcount表示正在讀的進程數目。由於只要有一個Reader進程在讀,便不允許Writer進程去寫。因此,僅當Readcount=0,表示尚無Reader進程在讀時,Reader進程才需要執行Wait(Wmutex)操作。若wait(Wmutex)操作成功,Reader進程便可去讀,相應地,做Readcount+1操作。
2. 利用信號量集機制解決讀者-寫者問題
這里的讀者—寫者問題,與前面的略有不同,它增加了一個限制,即最多只允許RN個讀者同時讀。為此,又引入了一個信號量L,並賦予其初值為RN,通過執行wait(L, 1, 1)操作來控制讀者的數目,每當有一個讀者進入時,就要先執行wait(L, 1, 1)操作,使L的值減1。當有RN個讀者進入讀后,L便減為0,第RN + 1個讀者要進入讀時,必然會因wait(L, 1, 1)操作失敗而阻塞。
2.6 進 程 通 信
進程通信是指進程之間的信息交換。由於進程的互斥與同步,需要在進程間交換一定的信息,故不少學者將它們也歸為進程通信,但只能把它們稱為低級進程通信。我們以信號量機制為例來說明,它們之所以低級的原因在於:① 效率低,生產者每次只能向緩沖池投放一個產品(消息),消費者每次只能從緩沖區中取得一個消息;② 通信對用戶不透明,OS只為進程之間的通信提供了共享存儲器。
在進程之間要傳送大量數據時,應當利用OS提供的高級通信工具,該工具最主要的特點是:
(1) 使用方便。OS隱藏了實現進程通信的具體細節,向用戶提供了一組用於實現高級通信的命令(原語),用戶可方便地直接利用它實現進程之間的通信。或者說,通信過程對用戶是透明的。這樣就大大減少了通信程序編制上的復雜性。
(2) 高效地傳送大量數據。用戶可直接利用高級通信命令(原語)高效地傳送大量的數據。
2.6.1 進程通信的類型
1. 共享存儲器系統(Shared-Memory System)
在共享存儲器系統中,相互通信的進程共享某些數據結構或共享存儲區,進程之間能夠通過這些空間進行通信。據此,又可把它們分成以下兩種類型:
(1) 基於共享數據結構的通信方式。 (2) 基於共享存儲區的通信方式。
2. 管道(pipe)通信系統
所謂“管道”,是指用於連接一個讀進程和一個寫進程以實現它們之間通信的一個共享文件,又名pipe文件。向管道(共享文件)提供輸入的發送進程(即寫進程)以字符流形式將大量的數據送入管道;而接受管道輸出的接收進程(即讀進程)則從管道中接收(讀)數據。由於發送進程和接收進程是利用管道進行通信的,故又稱為管道通信。這種方式首創於UNIX系統,由於它能有效地傳送大量數據,因而又被引入到許多其它操作系統中。
為了協調雙方的通信,管道機制必須提供以下三方面的協調能力:① 互斥,即當一個進程正在對pipe執行讀/寫操作時,其它(另一)進程必須等待。② 同步,指當寫(輸入)進程把一定數量(如4 KB)的數據寫入pipe,便去睡眠等待,直到讀(輸出)進程取走數據后再把它喚醒。當讀進程讀一空pipe時,也應睡眠等待,直至寫進程將數據寫入管道后才將之喚醒。③ 確定對方是否存在,只有確定了對方已存在時才能進行通信。
3. 消息傳遞系統(Message passing system)
在該機制中,進程不必借助任何共享存儲區或數據結構,而是以格式化的消息 (message)為單位,將通信的數據封裝在消息中,並利用操作系統提供的一組通信命令(原語),在進程間進行消息傳遞,完成進程間的數據交換。
基於消息傳遞系統的通信方式屬於高級通信方式,因其實現方式的不同,可進一步分成兩類:
(1) 直接通信方式 (2) 間接通信方式
4. 客戶機-服務器系統(Client-Server system)
1) 套接字(Socket)
套接字起源於20世紀70年代加州大學伯克利分校版本的UNIX(即BSD Unix),是UNIX 操作系統下的網絡通信接口。一開始,套接字被設計用在同一台主機上多個應用程序之間的通信(即進程間的通信),主要是為了解決多對進程同時通信時端口和物理線路的多路復用問題。隨着計算機網絡技術的發展以及UNIX 操作系統的廣泛使用,套接字已逐漸成為最流行的網絡通信程序接口之一。
2) 遠程過程調用和遠程方法調用
遠程過程(函數)調用RPC(Remote Procedure Call),是一個通信協議,用於通過網絡連接的系統。該協議允許運行於一台主機(本地)系統上的進程調用另一台主機(遠程)系統上的進程,而對程序員表現為常規的過程調用,無需額外地為此編程。如果涉及的軟件采用面向對象編程,那么遠程過程調用亦可稱做遠程方法調用。
實際上,遠程過程調用的主要步驟是:
(1) 本地過程調用者以一般方式調用遠程過程在本地關聯的客戶存根,傳遞相應的參數,然后將控制權轉移給客戶存根;
(2) 客戶存根執行,完成包括過程名和調用參數等信息的消息建立,將控制權轉移給本地客戶進程;
(3) 本地客戶進程完成與服務器的消息傳遞,將消息發送到遠程服務器進程;
(4) 遠程服務器進程接收消息后轉入執行,並根據其中的遠程過程名找到對應的服務器存根,將消息轉給該存根;
(5) 該服務器存根接到消息后,由阻塞狀態轉入執行狀態,拆開消息從中取出過程調用的參數,然后以一般方式調用服務器上關聯的過程;
(6) 在服務器端的遠程過程運行完畢后,將結果返回給與之關聯的服務器存根;
(7) 該服務器存根獲得控制權運行,將結果打包為消息,並將控制權轉移給遠程服務器進程;
(8) 遠程服務器進程將消息發送回客戶端;
(9) 本地客戶進程接收到消息后,根據其中的過程名將消息存入關聯的客戶存根,再將控制權轉移給客戶存根;
(10) 客戶存根從消息中取出結果,返回給本地調用者進程,並完成控制權的轉移。
2.6.2 消息傳遞通信的實現方式
1. 直接消息傳遞系統
在直接消息傳遞系統中采用直接通信方式,即發送進程利用OS所提供的發送命令(原語),直接把消息發送給目標進程。
1) 直接通信原語
(1) 對稱尋址方式。 (2) 非對稱尋址方式。
2) 消息的格式
在消息傳遞系統中所傳遞的消息,必須具有一定的消息格式。在單機系統環境中,由於發送進程和接收進程處於同一台機器中,有着相同的環境,所以消息的格式比較簡單,可采用比較短的定長消息格式,以減少對消息的處理和存儲開銷。該方式可用於辦公自動化系統中,為用戶提供快速的便箋式通信。但這種方式對於需要發送較長消息的用戶是不方便的。為此,可采用變長的消息格式,即進程所發送消息的長度是可變的。對於變長消息,系統無論在處理方面還是存儲方面,都可能會付出更多的開銷,但其優點在於方便了用戶。
3) 進程的同步方式
在進程之間進行通信時,同樣需要有進程同步機制,以使諸進程間能協調通信。不論是發送進程還是接收進程,在完成消息的發送或接收后,都存在兩種可能性,即進程或者繼續發送(或接收)或者阻塞。
4) 通信鏈路
為使在發送進程和接收進程之間能進行通信,必須在兩者之間建立一條通信鏈路。有兩種方式建立通信鏈路。第一種方式是:由發送進程在通信之前用顯式的“建立連接”命令(原語)請求系統為之建立一條通信鏈路,在鏈路使用完后拆除鏈路。
2. 信箱通信
1) 信箱的結構
信箱定義為一種數據結構。在邏輯上,可以將其分為兩個部分:
(1) 信箱頭 (2) 信箱體

圖2-16 雙向信箱示意圖
2) 信箱通信原語
系統為郵箱通信提供了若干條原語,分別用於:
(1) 郵箱的創建和撤消。 (2) 消息的發送和接收。
3) 信箱的類型
郵箱可由操作系統創建,也可由用戶進程創建,創建者是郵箱的擁有者。據此,可把郵箱分為以下三類:
(1) 私用郵箱。 (2) 公用郵箱。 (3) 共享郵箱。
2.6.3 直接消息傳遞系統實例
消息緩沖隊列通信機制首先由美國的Hansan提出,並在RC 4000系統上實現,后來被廣泛應用於本地進程之間的通信中。在這種通信機制中,發送進程利用Send原語將消息直接發送給接收進程;接收進程則利用Receive原語接收消息。
1. 消息緩沖隊列通信機制中的數據結構
(1) 消息緩沖區。 (2) PCB中有關通信的數據項。
2. 發送原語
發送進程在利用發送原語發送消息之前,應先在自己的內存空間設置一發送區a,如圖2-17所示,把待發送的消息正文、發送進程標識符、消息長度等信息填入其中,然后調用發送原語,把消息發送給目標(接收)進程。發送原語首先根據發送區a中所設置的消息長度a.size來申請一緩沖區i,接着,把發送區a中的信息復制到緩沖區i中。為了能將i掛在接收進程的消息隊列mq上,應先獲得接收進程的內部標識符j,然后將i掛在j.mq上。由於該隊列屬於臨界資源,故在執行insert操作的前后都要執行wait和signal操作。

圖2-17 消息緩沖通信
3. 接收原語
接收進程調用接收原語receive(b),從自己的消息緩沖隊列mq中摘下第一個消息緩沖區i,並將其中的數據復制到以b為首址的指定消息接收區內。
2.7 線程(Threads)的基本概念
2.7.1 線程的引入
如果說,在OS中引入進程的目的是為了使多個程序能並發執行,以提高資源利用率和系統吞吐量,那么,在操作系統中再引入線程,則是為了減少程序在並發執行時所付出的時空開銷,使OS具有更好的並發性。
1. 進程的兩個基本屬性
首先讓我們來回顧進程的兩個基本屬性:
① 進程是一個可擁有資源的獨立單位,一個進程要能獨立運行,它必須擁有一定的資源,包括用於存放程序正文、數據的磁盤和內存地址空間,以及它在運行時所需要的I/O設備、已打開的文件、信號量等;
② 進程同時又是一個可獨立調度和分派的基本單位,一個進程要能獨立運行,它還必須是一個可獨立調度和分派的基本單位。每個進程在系統中有唯一的PCB,系統可根據其PCB感知進程的存在,也可以根據其PCB中的信息,對進程進行調度,還可將斷點信息保存在其PCB中。反之,再利用進程PCB中的信息來恢復進程運行的現場。正是由於進程有這兩個基本屬性,才使進程成為一個能獨立運行的基本單位,從而也就構成了進程並發執行的基礎。
2. 程序並發執行所需付出的時空開銷
為使程序能並發執行,系統必須進行以下的一系列操作:
(1) 創建進程,系統在創建一個進程時,必須為它分配其所必需的、除處理機以外的所有資源,如內存空間、I/O設備,以及建立相應的PCB;
(2) 撤消進程,系統在撤消進程時,又必須先對其所占有的資源執行回收操作,然后再撤消PCB;
(3) 進程切換,對進程進行上下文切換時,需要保留當前進程的CPU環境,設置新選中進程的CPU環境,因而須花費不少的處理機時間。
3. 線程——作為調度和分派的基本單位
如何能使多個程序更好地並發執行,同時又盡量減少系統的開銷,已成為近年來設計操作系統時所追求的重要目標。有不少研究操作系統的學者們想到,要設法將進程的上述兩個屬性分開,由OS分開處理,亦即並不把作為調度和分派的基本單位也同時作為擁有資源的單位,以做到“輕裝上陣”;而對於擁有資源的基本單位,又不對之施以頻繁的切換。正是在這種思想的指導下,形成了線程的概念。
2.7.2 線程與進程的比較
1. 調度的基本單位 2. 並發性 3. 擁有資源 4. 獨立性 5. 系統開銷 6. 支持多處理機系統
1 進程與線程的一個簡單解釋 2 進程(process)和線程(thread)是操作系統的基本概念,但是它們比較抽象,不容易掌握。 3 4 最近,我讀到一篇材料,發現有一個很好的類比,可以把它們解釋地清晰易懂。 5 6 1. 7 8 9 10 計算機的核心是CPU,它承擔了所有的計算任務。它就像一座工廠,時刻在運行。 11 12 2. 13 14 15 16 假定工廠的電力有限,一次只能供給一個車間使用。也就是說,一個車間開工的時候,其他車間都必須停工。背后的含義就是,單個CPU一次只能運行一個任務。 17 18 3. 19 20 21 22 進程就好比工廠的車間,它代表CPU所能處理的單個任務。任一時刻,CPU總是運行一個進程,其他進程處於非運行狀態。 23 24 4. 25 26 27 28 一個車間里,可以有很多工人。他們協同完成一個任務。 29 30 5. 31 32 33 34 線程就好比車間里的工人。一個進程可以包括多個線程。 35 36 6. 37 38 39 40 車間的空間是工人們共享的,比如許多房間是每個工人都可以進出的。這象征一個進程的內存空間是共享的,每個線程都可以使用這些共享內存。 41 42 7. 43 44 45 46 可是,每間房間的大小不同,有些房間最多只能容納一個人,比如廁所。里面有人的時候,其他人就不能進去了。這代表一個線程使用某些共享內存時,其他線程必須等它結束,才能使用這一塊內存。 47 48 8. 49 50 51 52 一個防止他人進入的簡單方法,就是門口加一把鎖。先到的人鎖上門,后到的人看到上鎖,就在門口排隊,等鎖打開再進去。這就叫"互斥鎖"(Mutual exclusion,縮寫 Mutex),防止多個線程同時讀寫某一塊內存區域。 53 54 9. 55 56 57 58 還有些房間,可以同時容納n個人,比如廚房。也就是說,如果人數大於n,多出來的人只能在外面等着。這好比某些內存區域,只能供給固定數目的線程使用。 59 60 10. 61 62 63 64 這時的解決方法,就是在門口掛n把鑰匙。進去的人就取一把鑰匙,出來時再把鑰匙掛回原處。后到的人發現鑰匙架空了,就知道必須在門口排隊等着了。這種做法叫做"信號量"(Semaphore),用來保證多個線程不會互相沖突。 65 66 不難看出,mutex是semaphore的一種特殊情況(n=1時)。也就是說,完全可以用后者替代前者。但是,因為mutex較為簡單,且效率高,所以在必須保證資源獨占的情況下,還是采用這種設計。 67 68 11. 69 70 71 72 操作系統的設計,因此可以歸結為三點: 73 74 (1)以多進程形式,允許多個任務同時運行; 75 76 (2)以多線程形式,允許單個任務分成不同的部分運行; 77 78 (3)提供協調機制,一方面防止進程之間和線程之間產生沖突,另一方面允許進程之間和線程之間共享資源。 79 80 (完) 81 82 轉:http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html 83 84 85 86 這里有一段知乎上的解釋: 87 88 看見上面幾位的回答我真的是醉了。說幾句我的理解。 89 90 首先來一句概括的總論:進程和線程都是一個時間段的描述,是CPU工作時間段的描述。 91 92 下面細說背景: 93 CPU+RAM+各種資源(比如顯卡,光驅,鍵盤,GPS, 等等外設)構成我們的電腦,但是電腦的運行,實際就是CPU和相關寄存器以及RAM之間的事情。 94 95 一個最最基礎的事實:CPU太快,太快,太快了,寄存器僅僅能夠追的上他的腳步,RAM和別的掛在各總線上的設備完全是望其項背。那當多個任務要執行的時候怎么辦呢?輪流着來?或者誰優先級高誰來?不管怎么樣的策略,一句話就是在CPU看來就是輪流着來。 96 97 一個必須知道的事實:執行一段程序代碼,實現一個功能的過程介紹 ,當得到CPU的時候,相關的資源必須也已經就位,就是顯卡啊,GPS啊什么的必須就位,然后CPU開始執行。這里除了CPU以外所有的就構成了這個程序的執行環境,也就是我們所定義的程序上下文。當這個程序執行完了,或者分配給他的CPU執行時間用完了,那它就要被切換出去,等待下一次CPU的臨幸。在被切換出去的最后一步工作就是保存程序上下文,因為這個是下次他被CPU臨幸的運行環境,必須保存。 98 99 串聯起來的事實:前面講過在CPU看來所有的任務都是一個一個的輪流執行的,具體的輪流方法就是:先加載程序A的上下文,然后開始執行A,保存程序A的上下文,調入下一個要執行的程序B的程序上下文,然后開始執行B,保存程序B的上下文。。。。 100 101 ========= 重要的東西出現了======== 102 進程和線程就是這樣的背景出來的,兩個名詞不過是對應的CPU時間段的描述,名詞就是這樣的功能。 103 進程就是包換上下文切換的程序執行時間總和 = CPU加載上下文+CPU執行+CPU保存上下文 104 線程是什么呢? 105 進程的顆粒度太大,每次都要有上下的調入,保存,調出。如果我們把進程比喻為一個運行在電腦上的軟件,那么一個軟件的執行不可能是一條邏輯執行的,必定有多個分支和多個程序段,就好比要實現程序A,實際分成 a,b,c等多個塊組合而成。那么這里具體的執行就可能變成: 106 107 程序A得到CPU =》CPU加載上下文,開始執行程序A的a小段,然后執行A的b小段,然后再執行A的c小段,最后CPU保存A的上下文。 108 109 這里a,b,c的執行是共享了A的上下文,CPU在執行的時候沒有進行上下文切換的。這里的a,b,c就是線程,也就是說線程是共享了進程的上下文環境,的更為細小的CPU時間段。 110 111 到此全文結束,再一個總結: 112 113 進程和線程都是一個時間段的描述,是CPU工作時間段的描述,不過是顆粒大小不同。 114 115 來自 https://www.zhihu.com/question/25532384 116 好文要頂 關注我 收藏該文
2.7.3 線程的狀態和線程控制塊
1. 線程運行的三個狀態
與傳統的進程一樣,在各線程之間也存在着共享資源和相互合作的制約關系,致使線程在運行時也具有間斷性。相應地,線程在運行時也具有下述三種基本狀態:
(1) 執行狀態,表示線程已獲得處理機而正在運行;
(2) 就緒狀態,指線程已具備了各種執行條件,只須再獲得CPU便可立即執行;
(3) 阻塞狀態,指線程在執行中因某事件受阻而處於暫停狀態,例如,當一個線程執行從鍵盤讀入數據的系統調用時,該線程就被阻塞。
2. 線程控制塊TCB
如同每個進程有一個進程控制塊一樣,系統也為每個線程配置了一個線程控制塊TCB,將所有用於控制和管理線程的信息記錄在線程控制塊中。
3. 多線程OS中的進程屬性
通常在多線程OS中的進程都包含了多個線程,並為它們提供資源。OS支持在一個進程中的多個線程能並發執行,但此時的進程就不再作為一個執行的實體。多線程OS中的進程有以下屬性:
(1) 進程是一個可擁有資源的基本單位。 (2) 多個線程可並發執行。 (3) 進程已不是可執行的實體。
2.8 線 程 的 實 現
2.8.1 線程的實現方式
線程已在許多系統中實現,但各系統的實現方式並不完全相同。在有的系統中,特別是一些數據庫管理系統,如infomix所實現的是用戶級線程; 而另一些系統(如Macintosh和OS/2操作系統)所實現的是內核支持線程;還有一些系統如Solaris操作系統,則同時實現了這兩種類型的線程。
1. 內核支持線程KST(Kernel Supported Threads)
在OS中的所有進程,無論是系統進程還是用戶進程,都是在操作系統內核的支持下運行的,是與內核緊密相關的。而內核支持線程KST同樣也是在內核的支持下運行的,它們的創建、阻塞、撤消和切換等,也都是在內核空間實現的。為了對內核線程進行控制和管理,在內核空間也為每一個內核線程設置了一個線程控制塊,內核根據該控制塊而感知某線程的存在,並對其加以控制。當前大多數OS都支持內核支持線程。
這種線程實現方式主要有四個主要優點:
(1) 在多處理器系統中,內核能夠同時調度同一進程中的多個線程並行執行;
(2) 如果進程中的一個線程被阻塞了,內核可以調度該進程中的其它線程占有處理器運行,也可以運行其它進程中的線程;
(3) 內核支持線程具有很小的數據結構和堆棧,線程的切換比較快,切換開銷小;
(4) 內核本身也可以采用多線程技術,可以提高系統的執行速度和效率。
2. 用戶級線程ULT(User Level Threads)
用戶級線程是在用戶空間中實現的。對線程的創建、 撤消、同步與通信等功能,都無需內核的支持,即用戶級線程是與內核無關的。在一個系統中的用戶級線程的數目可以達到數百個至數千個。由於這些線程的任務控制塊都是設置在用戶空間,而線程所執行的操作也無需內核的幫助,因而內核完全不知道用戶級線程的存在。
使用用戶級線程方式有許多優點:
(1) 線程切換不需要轉換到內核空間。
(2) 調度算法可以是進程專用的。
(3) 用戶級線程的實現與OS平台無關,因為對於線程管理的代碼是屬於用戶程序的一部分,所有的應用程序都可以對之進行共享。
而用戶級線程方式的主要缺點則在於:
(1) 系統調用的阻塞問題。在基於進程機制的OS中,大多數系統調用將使進程阻塞,因此,當線程執行一個系統調用時,不僅該線程被阻塞,而且,進程內的所有線程會被阻塞。而在內核支持線程方式中,則進程中的其它線程仍然可以運行。
(2) 在單純的用戶級線程實現方式中,多線程應用不能利用多處理機進行多重處理的優點,內核每次分配給一個進程的僅有一個CPU,因此,進程中僅有一個線程能執行,在該線程放棄CPU之前,其它線程只能等待。
3. 組合方式
有些OS把用戶級線程和內核支持線程兩種方式進行組合,提供了組合方式ULT/KST 線程。在組合方式線程系統中,內核支持多個內核支持線程的建立、調度和管理,同時,也允許用戶應用程序建立、調度和管理用戶級線程。

圖2-18 多線程模型
2.8.2 線程的實現
1. 內核支持線程的實現
在僅設置了內核支持線程的OS中,一種可能的線程控制方法是,系統在創建一個新進程時,便為它分配一個任務數據區PTDA(Per Task Data Area),其中包括若干個線程控制塊TCB空間,如圖2-19所示。

圖2-19 任務數據區空間
2. 用戶級線程的實現
1) 運行時系統(Runtime System)
所謂“運行時系統”,實質上是用於管理和控制線程的函數(過程)的集合,其中包括用於創建和撤消線程的函數、線程同步和通信的函數,以及實現線程調度的函數等。正因為有這些函數,才能使用戶級線程與內核無關。運行時系統中的所有函數都駐留在用戶空間,並作為用戶級線程與內核之間的接口。
2) 內核控制線程
這種線程又稱為輕型進程LWP(Light Weight Process)。每一個進程都可擁有多個LWP,同用戶級線程一樣,每個LWP都有自己的數據結構(如TCB),其中包括線程標識符、優先級、狀態,另外還有棧和局部存儲區等。LWP也可以共享進程所擁有的資源。LWP可通過系統調用來獲得內核提供的服務,這樣,當一個用戶級線程運行時,只須將它連接到一個LWP上,此時它便具有了內核支持線程的所有屬性。這種線程實現方式就是組合方式。

圖2-20 利用輕型進程作為中間系統
2.8.3 線程的創建和終止
1. 線程的創建
應用程序在啟動時,通常僅有一個線程在執行,人們把線程稱為“初始化線程”,它的主要功能是用於創建新線程。在創建新線程時,需要利用一個線程創建函數(或系統調用),並提供相應的參數,如指向線程主程序的入口指針、堆棧的大小,以及用於調度的優先級等。在線程的創建函數執行完后,將返回一個線程標識符供以后使用。
2. 線程的終止
當一個線程完成了自己的任務(工作)后,或是線程在運行中出現異常情況而須被強行終止時,由終止線程通過調用相應的函數(或系統調用)對它執行終止操作。但有些線程(主要是系統線程),它們一旦被建立起來之后,便一直運行下去而不被終止。在大多數的OS中,線程被中止后並不立即釋放它所占有的資源,只有當進程中的其它線程執行了分離函數后,被終止的線程才與資源分離,此時的資源才能被其它線程利用。
習 題
1. 什么是前趨圖? 為什么要引入前趨圖?
2. 試畫出下面四條語句的前趨圖:
S1: a = x+y;
S2: b = z+1;
S3: c = a-b;
S4: w = c+1;
3. 為什么程序並發執行會產生間斷性特征?
4. 程序並發執行時為什么會失去封閉性和可再現性?
5. 在操作系統中為什么要引入進程的概念? 它會產生什么樣的影響?
6. 試從動態性、並發性和獨立性上比較進程和程序。
7. 試說明PCB的作用具體表現在哪幾個方面,為什么說PCB是進程存在的唯一標志?
8. PCB提供了進程管理和進程調度所需要的哪些信息?
9. 進程控制塊的組織方式有哪幾種?
10. 何謂操作系統內核? 內核的主要功能是什么?
11. 試說明進程在三個基本狀態之間轉換的典型原因。
12. 為什么要引入掛起狀態? 該狀態有哪些性質?13. 在進行進程切換時,所要保存的處理機狀態信息有哪些?
14. 試說明引起進程創建的主要事件。
15. 試說明引起進程被撤消的主要事件。
16. 在創建一個進程時所要完成的主要工作是什么?
17. 在撤消一個進程時所要完成的主要工作是什么?
18. 試說明引起進程阻塞或被喚醒的主要事件是什么?
19. 為什么要在OS中引入線程?
20. 試說明線程具有哪些屬性?
21. 試從調度性、並發性、擁有資源及系統開銷方面對進程和線程進行比較。
22. 線程控制塊TCB中包含了哪些內容?
23. 何謂用戶級線程和內核支持線程?
24. 試說明用戶級線程的實現方法。
25. 試說明內核支持線程的實現方法。
26. 多線程模型有哪幾種類型? 多對一模型有何優缺點?
線程:https://www.cnblogs.com/dreamroute/p/5207813.html

