




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
第4章 進程管理4.1進程4.2進程的運行模式4.3進程的描述與組織4.4進程控制4.5進程調(diào)度4.6進程的互斥與同步4.7進程通信4.8線程
4.1 進程
進程是現(xiàn)代操作系統(tǒng)的核心概念,它用來描述程序的執(zhí)行過程,是實現(xiàn)多任務(wù)操作系統(tǒng)的基礎(chǔ)。操作系統(tǒng)的其他所有內(nèi)容都是圍繞著進程展開的。4.1.1 程序的順序執(zhí)行與并發(fā)執(zhí)行1. 程序的順序執(zhí)行如果程序的各操作步驟之間是依序執(zhí)行的,程序與程序之間是串行執(zhí)行的,這種執(zhí)行程序的方式就稱為順序執(zhí)行。順序執(zhí)行是單道程序系統(tǒng)中的程序的運行方式。
程序的順序執(zhí)行具有如下特點:
(1)順序性:CPU嚴格按照程序規(guī)定的順序執(zhí)行,僅當一個操作結(jié)束后,下一個操作才能開始執(zhí)行。
(2)封閉性:程序在封閉的環(huán)境中運行,獨占全部系統(tǒng)資源。
(3)可再現(xiàn)性:程序執(zhí)行的結(jié)果與運行的時間和速度無關(guān),結(jié)果總是可再現(xiàn)的,即無論何時重復(fù)執(zhí)行該程序都會得到同樣的結(jié)果。
總的說來,這種執(zhí)行程序的方式簡單,且便于調(diào)試。但由于順序程序在運行時獨占全部系統(tǒng)資源,因而系統(tǒng)資源利用率很低。
1.程序的并發(fā)執(zhí)行
單道程序、封閉式運行是早期操作系統(tǒng)的標志,而多道程序并發(fā)運行是現(xiàn)代操作系統(tǒng)的基本特征。多個程序同時在系統(tǒng)中運行,使系統(tǒng)資源得到充分利用,系統(tǒng)效率大大提高。
程序的并發(fā)執(zhí)行是指若干個程序或程序段同時運行,它們的執(zhí)行在時間上是重疊的。
程序的并發(fā)執(zhí)行有以下特點:
(1)間斷性:并發(fā)程序之間因競爭資源而相互制約,導(dǎo)致程序運行過程的間斷。
(2)沒有封閉性:當多個程序共享系統(tǒng)資源時,一個程序的運行受其他程序的影響,其運行過程和結(jié)果不完全由自身決定。
(3)不可再現(xiàn)性:由于沒有了封閉性,并發(fā)程序的執(zhí)行結(jié)果與執(zhí)行的時機以及執(zhí)行的速度有關(guān),結(jié)果往往不可再現(xiàn)。
并發(fā)執(zhí)行程序雖然可以提高系統(tǒng)的資源利用率和系統(tǒng)吞吐量,但程序的行為變得復(fù)雜和不確定。
2.并發(fā)執(zhí)行的潛在問題
程序在并發(fā)執(zhí)行時會導(dǎo)致執(zhí)行結(jié)果的不可再現(xiàn)性,這是多道程序系統(tǒng)必須解決的問題。下面的例子說明了并發(fā)執(zhí)行過程對運行結(jié)果的影響,從中可以了解產(chǎn)生問題的原因。
設(shè)某停車場使用程序控制電子公告牌來顯示空閑車位數(shù)。空閑車位數(shù)用一個計數(shù)器C記錄。車輛入庫時執(zhí)行程序A,車輛出庫時執(zhí)行程序B,它們都要更新同一個計數(shù)器C。程序A和程序B的片段如圖4-1所示。圖4-1兩程序并發(fā)運行,訪問計數(shù)器C
由于車輛出入庫的時間是隨機的,因此程序A與程序B的運行時間也就是不確定的。當出入庫同時發(fā)生時,將使兩程序在系統(tǒng)中并發(fā)運行。它們各運行一次后C計數(shù)器的值應(yīng)保持不變。但結(jié)果可能不是如此。圖4-2展示了并發(fā)執(zhí)行可能的結(jié)果。圖4-2并發(fā)程序的執(zhí)行時序影響執(zhí)行結(jié)果
4.1.2 進程的概念
1. 進程
進程(process)是一個可并發(fā)執(zhí)行的程序在一個數(shù)據(jù)集上的一次運行。
進程與程序的概念既相互關(guān)聯(lián)又相互區(qū)別。程序是進程的一個組成部分,是進程的執(zhí)行文本,而進程是程序的執(zhí)行過程。兩者的關(guān)系可以比喻為電影與膠片的關(guān)系:膠片是靜態(tài)的,是電影的放映素材;而電影是動態(tài)的,一場電影就是膠片在放映機上的一次“運行”。對進程而言,程序是靜態(tài)的指令集合,可以永久存在;而進程是個動態(tài)的過程實體,動態(tài)地產(chǎn)生、發(fā)展和消失。
此外,進程與程序之間也不是一一對應(yīng)的關(guān)系,表現(xiàn)在:
(1)一個進程可以順序執(zhí)行多個程序,如同一場電影可以連續(xù)播放多部膠片一樣。
(2)一個程序可以對應(yīng)多個進程,就像一本膠片可以放映多場電影一樣。
2. 進程的特性
進程與程序的不同主要體現(xiàn)在進程有一些程序所沒有的特性。要真正理解進程,首先應(yīng)了解它的基本性質(zhì)。進程具有以下幾個基本特性:
(1)動態(tài)性:進程由“創(chuàng)建”而產(chǎn)生,由“撤銷”而消亡,因“調(diào)度”而運行,因“等待”而停頓。
(2)并發(fā)性:在同一時間段內(nèi)有多個進程在系統(tǒng)中活動。它們宏觀上是在并發(fā)運行,而微觀上是在交替運行。
(3)獨立性:進程是可以獨立運行的基本單位,是操作系統(tǒng)分配資源和調(diào)度管理的基本對象。
(4)異步性:每個進程都獨立地執(zhí)行,各自按照不可預(yù)知的速度向前推進。
3. 進程的基本狀態(tài)
進程有如下3個基本的狀態(tài):
(1)就緒態(tài):進程已經(jīng)分配到了除CPU之外的所有資源,這時的進程狀態(tài)稱為就緒狀態(tài)。處于就緒態(tài)的進程,一旦獲得CPU便可立即執(zhí)行。
(2)運行態(tài):進程已經(jīng)獲得CPU,正在運行,這時的進程狀態(tài)稱為運行態(tài)。
(3)等待態(tài):進程因某種資源不能滿足,或希望的某事件尚未發(fā)生而暫停執(zhí)行時,則稱它處于等待態(tài)。
4. 進程狀態(tài)的轉(zhuǎn)換
進程誕生之初處于就緒狀態(tài),在其隨后的生存期間內(nèi)不斷地從一個狀態(tài)轉(zhuǎn)換到另一個狀態(tài),最后在運行狀態(tài)結(jié)束。圖4-3所示是一個進程的狀態(tài)轉(zhuǎn)換圖。圖4-3進程的狀態(tài)轉(zhuǎn)換圖
引起狀態(tài)轉(zhuǎn)換的原因如下:
運行態(tài)→等待態(tài):正在執(zhí)行的進程因為等待某事件而無法執(zhí)行下去,比如,進程申請某種資源,而該資源恰好被其他進程占用,則該進程將交出CPU,進入等待狀態(tài)。
等待態(tài)→就緒態(tài):處于等待狀態(tài)的進程,若其所申請的資源得到滿足,則系統(tǒng)將資源分配給它,并將其狀態(tài)變?yōu)榫途w態(tài)。
運行態(tài)→就緒態(tài):正在執(zhí)行的進程的時間片用完了,或者有更高優(yōu)先級的進程到來,系統(tǒng)會暫停該進程的運行,使其進入就緒態(tài),然后調(diào)度其他進程運行。
就緒態(tài)→運行態(tài):處于就緒狀態(tài)的進程,當被進程調(diào)度程序選中后,即進入CPU運行。此時該進程的狀態(tài)變?yōu)檫\行態(tài)。
4.1.3 進程控制塊
進程由程序、數(shù)據(jù)和進程控制塊三個基本部分組成。程序是進程執(zhí)行的可執(zhí)行代碼,數(shù)據(jù)是進程所處理的對象,進程控制塊用于記錄有關(guān)進程的各種信息。它們存在于內(nèi)存,其內(nèi)容會隨著執(zhí)行過程的進展而不斷變化。
進程控制塊(ProcessControlBlock,PCB)是為管理進程而設(shè)置的一個數(shù)據(jù)結(jié)構(gòu),用于記錄進程的相關(guān)信息。
PCB記錄了有關(guān)進程的所有信息,主要包括以下4方面的內(nèi)容:
1. 進程描述信息
進程描述信息用于記錄一個進程的標識信息和身份特征,如家族關(guān)系和歸屬關(guān)系等。
2. 進程控制與調(diào)度信息進程的運行需要由系統(tǒng)進行控制和調(diào)度。
3. 資源信息
進程的運行需要占用一些系統(tǒng)資源,必要的資源包括進程的地址空間、要訪問的文件和設(shè)備以及要處理的信號等。進程是系統(tǒng)分配資源的基本單位。
4. 現(xiàn)場信息
進程現(xiàn)場也稱為進程上下文(processcontext),包括CPU的各個寄存器的值。
4.1.4 Linux系統(tǒng)中的進程
在Linux系統(tǒng)中,進程也稱為任務(wù)(task),兩者的概念是一致的。
1. Linux進程的狀態(tài)
Linux系統(tǒng)的進程有5種基本狀態(tài),即可執(zhí)行態(tài)、可中斷睡眠態(tài)、不可中斷睡眠態(tài)、暫停態(tài)和僵死態(tài)。狀態(tài)轉(zhuǎn)換圖如圖4-4所示。圖4-4?Linux系統(tǒng)的進程狀態(tài)轉(zhuǎn)換圖
Linux進程的基本狀態(tài)定義如下:
(1)可執(zhí)行態(tài)(runnable):可執(zhí)行態(tài)包含了上述狀態(tài)圖中的運行和就緒兩種狀態(tài)。
(2)睡眠態(tài)(sleeping):即等待態(tài)。此時進程正在等待某個事件或某個資源。
(3)暫停態(tài)(stopped或traced):處于暫停態(tài)的進程是由運行態(tài)轉(zhuǎn)換而來的,等待某種特殊處理。
(4)僵死態(tài)(zombie):進程運行結(jié)束或因某些原因被終止時,它將釋放除PCB外的所有資源。
2. Linux進程的狀態(tài)轉(zhuǎn)換過程
Linux進程的狀態(tài)轉(zhuǎn)換過程如下:
新創(chuàng)建的進程處于可執(zhí)行的就緒態(tài),等待調(diào)度執(zhí)行。
處于可執(zhí)行態(tài)的進程在就緒態(tài)和運行態(tài)之間輪回。就緒態(tài)的進程一旦被調(diào)度程序選中,就進入運行狀態(tài)。當進程的時間片耗盡或有更高優(yōu)先級的進程就緒時,調(diào)度程序?qū)⑦x擇新的進程進入CPU運行。被換下的進程將轉(zhuǎn)入就緒態(tài),等待下一次的調(diào)度。處于此輪回的進程在運行與就緒之間不斷地高速切換,可謂瞬息萬變。因此,對觀察者(系統(tǒng)與用戶)來說,將此輪回概括為一個相對穩(wěn)定的可執(zhí)行態(tài)才有意義。
運行態(tài)、睡眠態(tài)和就緒態(tài)形成一個回路。
運行態(tài)、暫停態(tài)和就緒態(tài)也構(gòu)成一個回路。
處于運行態(tài)的進程執(zhí)行結(jié)束后,進入僵死態(tài)。
3. Linux的進程描述符
Linux系統(tǒng)用task_struct結(jié)構(gòu)來記錄進程的信息,稱為進程描述符,也就是進程控制塊PCB。task_struct中的字段很多,主要包括以下內(nèi)容:
進程標識號(pid):標識該進程的一個整數(shù)。
認證信息(cred):包含進程的屬主和屬組的標識號uid、gid。
家族關(guān)系(parent、children、sibling):關(guān)聯(lián)父進程、子進程及兄弟進程的鏈接指針。
鏈接指針(tasks):將進程鏈入進程鏈表的指針。
狀態(tài)(state):進程當前的狀態(tài)。
調(diào)度信息(policy、prio、se、rt):調(diào)度使用的調(diào)度策略、優(yōu)先級和調(diào)度實體等。
記時信息(start_time、utime、stime):進程建立的時間以及執(zhí)行用戶代碼與系統(tǒng)代碼的累計時間。
信號信息(signal、sighand):進程收到的信號以及使用的信號處理程序。
退出碼(exit_code):進程運行結(jié)束后的退出代碼,供父進程查詢用。
文件系統(tǒng)信息(fs、files):包括文件系統(tǒng)及打開文件的信息。
地址空間信息(mm):進程的地址空間。
硬件現(xiàn)場信息(thread):進程切換時保存的CPU寄存器的內(nèi)容。
運行信息(thread_info):有關(guān)進程運行環(huán)境、狀況的CPU相關(guān)信息。
4. 查看進程的信息
查看進程信息的命令是ps(processstatus)命令。該命令可查看記錄在進程描述符task_struct中的幾乎所有信息。
【說明】
(1)默認只顯示在本終端上運行的進程,除非指定了-e、a、x等選項。
(2)沒有指定顯示格式時,采用以下默認格式,分4列顯示:
(3)指定-f選項時,以全格式,分8列顯示:
例4.1?ps命令用法示例。
4.2 進程的運行模式
4.2.1 操作系統(tǒng)的內(nèi)核操作系統(tǒng)的內(nèi)核是硬件的直接操控者,因此與硬件的架構(gòu)密切相關(guān)。對PC機來說,x86是32位PC機的硬件架構(gòu),x86-64是在x86的基礎(chǔ)上擴展而成的64位架構(gòu),簡稱為x64。這兩種架構(gòu)也是目前最為流行的硬件平臺。
1. x86/x64CPU的執(zhí)行模式
CPU的基本功能就是執(zhí)行指令。通常,CPU指令集中的指令可以分為兩類,即特權(quán)指令和非特權(quán)指令。特權(quán)指令是指那些具有特殊權(quán)限的指令。這類指令可以訪問系統(tǒng)中所有寄存器、內(nèi)存單元和I/O端口,修改系統(tǒng)的關(guān)鍵設(shè)置。
2. 操作系統(tǒng)內(nèi)核
一個完整的操作系統(tǒng)由一個內(nèi)核(kernel)和一些系統(tǒng)服務(wù)程序構(gòu)成。內(nèi)核是操作系統(tǒng)的核心,它負責(zé)最基本的資源管理和硬件控制工作,為進程提供運行的環(huán)境。內(nèi)核在系統(tǒng)引導(dǎo)時載入并常駐內(nèi)存。它運行在核心態(tài),因而能夠訪問所有的系統(tǒng)資源。
從進程的角度看,內(nèi)核的功能有兩個:一是支持進程的運行,包括為進程分配資源,控制和調(diào)度進程的運行;二是為進程提供服務(wù),也就是提供一些內(nèi)核函數(shù)(稱為系統(tǒng)調(diào)用)供進程調(diào)用使用。
3. Linux系統(tǒng)的內(nèi)核
圖4-5是Linux系統(tǒng)的體系結(jié)構(gòu)。系統(tǒng)分為3層:最底層是硬件層,包括了各種系統(tǒng)硬件和設(shè)備。硬件層之上是內(nèi)核,它形成了對硬件的第一層包裝。對下,它管理和控制硬件;對上,它提供系統(tǒng)服務(wù)。用戶層由系統(tǒng)的核外程序和用戶程序組成,它們都以用戶進程的方式運行在核心之上,為用戶提供更高層次的系統(tǒng)包裝。圖4-5?Linux系統(tǒng)的內(nèi)核結(jié)構(gòu)
Linux系統(tǒng)的內(nèi)核主要由以下成分構(gòu)成:
(1)系統(tǒng)調(diào)用接口:這是進程與內(nèi)核的接口,進程通過此接口調(diào)用內(nèi)核的功能。
(2)進程管理子系統(tǒng):負責(zé)支持、控制和調(diào)度進程的運行。該子系統(tǒng)包括以下模塊:
核心管理模塊kernel:管理CPU,調(diào)度和協(xié)調(diào)進程的運行。
進程通信模塊ipc:實現(xiàn)進程間的本地通信。
內(nèi)存管理模塊mm:管理內(nèi)存和進程的地址空間。
(3)文件與I/O子系統(tǒng):負責(zé)管理文件、設(shè)備和I/O操作。該子系統(tǒng)包括以下模塊:
文件系統(tǒng)模塊fs:為進程提供訪問文件和設(shè)備的服務(wù)。
網(wǎng)絡(luò)通信模塊net:管理網(wǎng)絡(luò)接口設(shè)備,提供進程間的網(wǎng)絡(luò)通信服務(wù)。
設(shè)備驅(qū)動模塊drivers:驅(qū)動設(shè)備的運行。
(4)硬件控制接口:提供與硬件平臺的接口,負責(zé)控制硬件并響應(yīng)和處理中斷。
4.2.2 中斷與系統(tǒng)調(diào)用
由圖4-5可以看出,內(nèi)核與外界的接口是來自用戶層的系統(tǒng)調(diào)用和來自硬件層的中斷,而系統(tǒng)調(diào)用本身也是一種特殊的中斷??梢哉f內(nèi)核是中斷驅(qū)動的,它的主要功能就體現(xiàn)在系統(tǒng)調(diào)用和中斷處理中。
1. 中斷
在現(xiàn)代系統(tǒng)中,CPU與各種設(shè)備是并發(fā)工作的。
2. 系統(tǒng)調(diào)用
一般的中斷都是源自CPU外部的事件,但還有一種特殊的中斷,其中斷源來自CPU內(nèi)部,是在CPU執(zhí)行了某個特殊指令時引發(fā)的。
系統(tǒng)調(diào)用是系統(tǒng)內(nèi)核提供給用戶進程的一組特殊的函數(shù)。
系統(tǒng)調(diào)用是通過陷入機制實現(xiàn)的。
4.2.3 進程的運行模式
Linux的內(nèi)核運行在核心態(tài),而用戶進程則只能運行在用戶態(tài)。從用戶態(tài)轉(zhuǎn)換為核心態(tài)的唯一途徑是中斷(包括陷入)。一旦CPU響應(yīng)了中斷,就將CPU的狀態(tài)切換到核心態(tài),待中斷處理結(jié)束返回時,再將CPU狀態(tài)切回到用戶態(tài)。
由于進程在其運行期間經(jīng)常會被中斷打斷,也經(jīng)常需要調(diào)用系統(tǒng)調(diào)用函數(shù),因此CPU會在用戶態(tài)與核心態(tài)之間來回地切換。在進行通常的計算和處理時,進程運行在用戶態(tài)。執(zhí)行系統(tǒng)調(diào)用或中斷處理程序時則進入核心態(tài)。圖4-6描述了進程在運行過程中的模式切換。圖4-6用戶進程的運行模式切換
4.3 進程的描述與組織
4.3.1 進程的資源1. 進程的地址空間進程的一個重要構(gòu)成成分是進程映像,即進程所執(zhí)行的代碼、使用的數(shù)據(jù)和堆棧等。為了容納進程映像,每個進程都有一個自己的地址空間,這是進程運行的必備條件。進程有用戶態(tài)和核心態(tài)兩種運行模式,不同模式下可訪問的地址空間也不相同。因此進程的地址空間被劃分為用戶空間和內(nèi)核空間兩部分,如圖4-7所示。圖4-7?Linux進程的地址空間
2. 進程的文件與設(shè)備
文件是信息的長久保存形式,應(yīng)用程序經(jīng)常要使用或處理文件。此外,應(yīng)用程序還需要使用設(shè)備來與外界傳輸數(shù)據(jù)。因此文件和設(shè)備都是進程的常用資源。在Linux系統(tǒng)中設(shè)備被當作文件來處理,因此兩者都由文件系統(tǒng)來管理。
3. 進程的信號通信
進程并非孤立地在運行。它需要能夠接收和處理系統(tǒng)或其他進程發(fā)來的信號,這些信號可能是通知它某個事件或控制命令,比如暫停運行、終止運行等。進程通過設(shè)定的信號處理程序來對信號做出響應(yīng)。為實現(xiàn)信號通信,進程需要擁有信號隊列以及信號處理程序。
4.3.2 進程的描述結(jié)構(gòu)
如前所述,進程描述符task_struct是進程的PCB,它記錄了進程的所有必要信息。task_struct結(jié)構(gòu)體中的字段較多,部分主要字段在4.1.4節(jié)中做了說明。本節(jié)重點描述進程描述符與內(nèi)核棧和幾個主要資源的連接結(jié)構(gòu),見圖4-8。圖4-8?Linux進程的描述結(jié)構(gòu)
4.3.3 進程的組織
管理進程就是管理進程的PCB。一個系統(tǒng)中通??赡軗碛袛?shù)百乃至上千個進程,為了有效地管理如此多的PCB,系統(tǒng)需要采用適當?shù)姆绞綄⑺鼈兘M織在一起。通常采用的組織結(jié)構(gòu)有數(shù)組、散列表和鏈表3種方式。
Linux系統(tǒng)采用了多種方式來組織進程的描述符task_struct,主要有以下幾種:
1. 進程鏈表
系統(tǒng)將所有進程的描述符鏈成一個雙向循環(huán)鏈表。
2. PID散列表
在許多情況下,內(nèi)核需要根據(jù)進程的標識號PID來查找進程。
3. 進程樹鏈表
Linux系統(tǒng)的進程之間存在著父子和兄弟關(guān)系。
4. 可執(zhí)行隊列
為了方便進程的調(diào)度,系統(tǒng)把所有處于可執(zhí)行狀態(tài)的進程組織成可執(zhí)行隊列。
5. 等待隊列
進程因不同的原因而睡眠。例如,等待磁盤操作的數(shù)據(jù),等待某系統(tǒng)資源可用或等待固定的時間間隔。
4.4 進程控制
進程控制是指對進程的生命周期進行有效的管理,實現(xiàn)進程的創(chuàng)建、撤銷以及進程各狀態(tài)之間的轉(zhuǎn)換等控制功能。進程控制的目標是使多進程能夠平穩(wěn)高效地并發(fā)執(zhí)行,充分共享系統(tǒng)資源。
4.4.1 進程控制的功能
進程控制的功能是控制進程在整個生命周期中各種狀態(tài)之間的轉(zhuǎn)換,但不包括就緒態(tài)與運行態(tài)之間的轉(zhuǎn)換,因為那是由進程調(diào)度來實現(xiàn)的。進程控制的任務(wù)主要有以下幾個:
1.創(chuàng)建進程
2.撤銷進程
3.阻塞進程
4.喚醒進程
4.4.2 Linux系統(tǒng)的進程控制
在Linux系統(tǒng)中,進程控制的功能是由內(nèi)核的進程管理子系統(tǒng)實現(xiàn)的,用戶進程可以通過內(nèi)核提供的系統(tǒng)調(diào)用來實現(xiàn)對進程的控制。
1. 進程的創(chuàng)建與映像更換
進程不能憑空出世,它是由另一個進程創(chuàng)建的。新創(chuàng)建的進程稱為子進程,創(chuàng)建子進程的進程稱為父進程。
1) 創(chuàng)建進程
創(chuàng)建一個進程的系統(tǒng)調(diào)用是fork(),方法是按父進程復(fù)制一個子進程。fork()的主要操作是:為子進程分配一個PID、內(nèi)核棧和task_struct結(jié)構(gòu),將thread_info和task_struct用指針連接起來;將父進程的內(nèi)核棧和task_struct結(jié)構(gòu)中的資源描述等內(nèi)容復(fù)制到子進程的對應(yīng)結(jié)構(gòu)中;將PID和家族關(guān)系等信息填入task_struct結(jié)構(gòu),初始化那些非繼承性的數(shù)據(jù),如時間片、運行統(tǒng)計數(shù)據(jù)等。至此,子進程的描述符已建立完成(如圖4-8所示)。
fork()系統(tǒng)調(diào)用
【功能】創(chuàng)建一個新的子進程。
【調(diào)用格式】intfork();
【返回值】
0 向子進程返回的返回值,總為0。
>0 向父進程返回的返回值,它是子進程的PID。
-1 創(chuàng)建失敗。
【說明】若fork()調(diào)用成功,它向父進程返回子進程的PID,并向新建的子進程返回0。
圖4-9描述了fork()系統(tǒng)調(diào)用的執(zhí)行結(jié)果圖4-9?fork()系統(tǒng)調(diào)用的執(zhí)行結(jié)果
如果程序中不考慮fork()的返回值的話,則父子進程的行為就完全一樣了。但創(chuàng)建一個子進程的目的是想讓它做另一件事。所以,通常的編程方法是在fork()調(diào)用后判斷fork()的返回值,分別為父進程和子進程設(shè)計不同的執(zhí)行分支。這樣,父子進程執(zhí)行的雖是同一個代碼,執(zhí)行路線卻不同。圖4-10描述了用fork()創(chuàng)建子進程的標準流程。圖4-10用fork()創(chuàng)建子進程
例4.2一個簡單的fork_test程序。
注:程序中的getpid()是一個系統(tǒng)調(diào)用,它返回本進程的PID。該程序運行時,父子進程會分別輸出自己的信息。由于是并發(fā)運行,輸出的先后順序并不確定,因此結(jié)果可能如下所示:
2)更換進程映像
新創(chuàng)建的子進程執(zhí)行的是與父進程相同的代碼。然而,通常我們需要的是讓新進程執(zhí)行另外一個程序。
exec()系統(tǒng)調(diào)用的功能是根據(jù)參數(shù)指定的路徑名找到可執(zhí)行文件,把它裝入進程的地址空間,覆蓋原來的進程映像,從而形成一個不同于父進程的全新的子進程。
【參數(shù)】
(1)可執(zhí)行文件:以p結(jié)尾的函數(shù)用file指定,只需指定文件名,如ls;其他函數(shù)用path指定,必須是絕對路徑名,如/bin/ls。
(2)運行參數(shù):以execv開頭的函數(shù)用argv[]字符串數(shù)組傳遞運行參數(shù);以execl開頭的函數(shù)用若干個arg字符串指定運行參數(shù)。
(3)運行環(huán)境:以e結(jié)尾的函數(shù)使用envp[]數(shù)組來指定傳遞給應(yīng)用程序的環(huán)境變量;其他的函數(shù)沒有這個參數(shù),它們將使用默認的環(huán)境變量。
與一般的函數(shù)不同,exec()是“一次調(diào)用,零次返回”,因為調(diào)用成功后,進程的映像已經(jīng)被替換,無處可回了。圖4-11描述了用exec()系統(tǒng)調(diào)用更換進程映像的流程。子進程開始運行后,立即調(diào)用exec(),變身成功后即開始執(zhí)行新的程序了。圖4-11用exec()更換進程的映像
例4.3一個簡單的fork-exec_test程序。
3)寫時復(fù)制技術(shù)
創(chuàng)建新進程的效率決定了系統(tǒng)快速執(zhí)行任務(wù)的能力。傳統(tǒng)的fork()系統(tǒng)調(diào)用是將父進程的映像復(fù)制給新的子進程,這個操作是耗時的。如果新進程創(chuàng)立后立即調(diào)用exec(),則剛復(fù)制進來的進程映像又會立刻被覆蓋掉,這顯然是非常低效的。為了優(yōu)化fork()的性能,Linux使用了“寫時復(fù)制”(copyonwrite)技術(shù),就是當fork()完成后并不立刻復(fù)制父進程的映像內(nèi)容,而是讓子進程共享父進程的同一個副本,直到遇到有一方執(zhí)行寫入操作(即修改了映像)時才進行復(fù)制,使父子進程擁有各自獨立的副本。也就是說,復(fù)制操作被推遲到真正需要的時候才進行。
2. 進程的終止與等待
1)進程的終止與退出碼
導(dǎo)致一個進程終止運行的方式有兩種:一是程序中使用退出語句主動退出,我們稱其為正常終止;另一種是被某個信號殺死,例如在進程運行時按Ctrl+c鍵終止其運行,這稱為非正常終止。
用C語言編程時,可以通過以下4種方式主動退出:
(1)調(diào)用exit(status)函數(shù)來結(jié)束程序;
(2)在main()函數(shù)中用returnstatus語句結(jié)束;
(3)在main()函數(shù)中用return語句結(jié)束;
(4)程序執(zhí)行至main()函數(shù)結(jié)束。
2)終止進程
進程無論以哪種方式退出,最終都會調(diào)用exit()系統(tǒng)調(diào)用,終止自己的運行。exit()系統(tǒng)調(diào)用執(zhí)行以下操作:釋放進程所占有的資源,只保留其描述符和內(nèi)核棧;向進程描述符中寫入進程退出碼和一些統(tǒng)計信息;置進程狀態(tài)為“僵死態(tài)”;如果有子進程的話就為它們找一個新的父進程來“領(lǐng)養(yǎng)”;通知父進程回收自己;最后調(diào)用進程調(diào)度程序切換進程。
3)等待與回收進程
僵尸進程的最后回收工作一般由其父進程負責(zé)。這是因為在許多情況下父進程需要了解子進程的執(zhí)行結(jié)果?;厥眨╮eap)的主要操作是從僵尸子進程的描述符中收集必要的信息,然后撤銷其剩余資源,即進程描述符和內(nèi)核棧。至此子進程徹底消失。圖4-12用wait實現(xiàn)進程的等待
例4.4一個簡單的wait-exit_test程序。
4)子進程的回收策略
子進程結(jié)束后留下僵尸是為了讓父進程采集運行信息,只有經(jīng)父進程回收后僵尸才能消失。如果因某種原因父進程未能回收子進程,就會造成僵尸積累,占用系統(tǒng)資源(主要是PID號),嚴重時會導(dǎo)致fork()失敗。這就是僵尸問題。
3. 進程的阻塞與喚醒
運行中的進程若需要等待一個特定事件的發(fā)生而不能繼續(xù)運行下去,則主動放棄CPU,進入睡眠態(tài)。等待的事件可能是一段時間、從磁盤上讀出的文件數(shù)據(jù)、來自鍵盤的輸入或是某個硬件事件。進程通過調(diào)用內(nèi)核函數(shù)來阻塞自己。阻塞進程的操作步驟是:建立一個等待隊列的節(jié)點,填入本進程的信息,將它鏈入相應(yīng)的等待隊列中;將進程的狀態(tài)置為睡眠態(tài),調(diào)用進程調(diào)度程序;進程調(diào)度程序?qū)⑵鋸目蓤?zhí)行隊列中刪去,并選擇其他進程運行。具體的實現(xiàn)策略通常還有些優(yōu)化,比如進程在即將改變狀態(tài)前先檢測等待條件,如果條件滿足則不必進入睡眠態(tài)。
被喚醒的進程通常還要判斷一下該事件是否滿足自己的等待條件,比如結(jié)束的子進程是否是自己所等待的子進程。如果是等待的事件則進行處理,然后繼續(xù)運行;不是則再次睡眠,等待下一次喚醒。處于可中斷睡眠態(tài)的進程也可以被信號喚醒。被信號喚醒稱為“偽”喚醒,即喚醒并非因為等待的事件發(fā)生。偽喚醒的進程在處理完信號后可能會改變狀態(tài),也可能會再次睡眠。
4.4.3 Shell命令的執(zhí)行過程
Shell程序的功能是執(zhí)行Shell命令。執(zhí)行命令的主要方式是創(chuàng)建一個子進程,讓這個子進程來執(zhí)行命令的映像文件。因此,Shell進程是所有在其下執(zhí)行的命令的父進程。圖4-13示意了Shell執(zhí)行命令的流程,從中可以看到一個進程從誕生到消失的整個過程。圖4-13?Shell命令的執(zhí)行過程
4.5 進程調(diào)度
4.5.1 進程調(diào)度的基本原理1. 進程調(diào)度的功能進程調(diào)度的功能是按照一定的策略把CPU分配給就緒進程,使它們輪流地使用CPU運行。進程調(diào)度實現(xiàn)了進程就緒態(tài)與運行態(tài)之間的轉(zhuǎn)換。進程調(diào)度的功能由內(nèi)核中的進程調(diào)度程序?qū)崿F(xiàn)。當正運行的進程因某種原因放棄CPU時,進程調(diào)度程序就會被調(diào)用。
調(diào)度操作包括以下兩項內(nèi)容:
(1)選擇進程:即按一定的調(diào)度算法,從就緒進程隊列中選一個進程。
(2)切換進程:為換下的進程保留現(xiàn)場,為選中的進程恢復(fù)現(xiàn)場,使其運行。
2. 進程調(diào)度的算法
進程調(diào)度的算法用于實現(xiàn)對CPU資源的分配策略,也就是決定什么時刻讓什么進程運行。調(diào)度算法是系統(tǒng)效率的關(guān)鍵,它直接決定著系統(tǒng)最本質(zhì)的性能指標,如響應(yīng)速度、吞吐量等。進程調(diào)度算法的目標首先是要充分發(fā)揮CPU的處理能力,滿足進程對CPU的需求。此外還要盡量做到公平對待每個進程,使它們都能得到合理的運行機會。
常用的調(diào)度算法有:
(1)先進先出法:按照進程在可執(zhí)行隊列中的先后次序來調(diào)度。
(2)短進程優(yōu)先法:按照進程的長短,優(yōu)先調(diào)度短進程運行。
(3)時間片輪轉(zhuǎn)法:進程按規(guī)定的時間片輪流使用CPU。
(4)優(yōu)先級調(diào)度法:為每個進程設(shè)置優(yōu)先級,調(diào)度時優(yōu)先選擇優(yōu)先級高的進程運行,使緊迫的任務(wù)可以優(yōu)先得到處理。
3. 進程調(diào)度的時機
引發(fā)進程調(diào)度的情況可歸納為下面幾種:
(1)當前進程放棄CPU,轉(zhuǎn)入睡眠、暫?;蚪┧缿B(tài);
(2)當前進程讓出CPU,轉(zhuǎn)入就緒態(tài);
(3)當前進程的時間片用盡;
(4)有更高優(yōu)先級的進程就緒。
4.5.2 Linux系統(tǒng)的進程調(diào)度
Linux系統(tǒng)的進程調(diào)度簡潔而高效。尤其是新版內(nèi)核采用的新調(diào)度算法,在高負載、多CPU及桌面系統(tǒng)中都執(zhí)行得極為出色。
1. 進程調(diào)度的信息
Linux的進程描述符中記錄了與進程調(diào)度相關(guān)的信息,主要有:
(1)調(diào)度策略(policy):對進程的調(diào)度算法,決定了調(diào)度程序應(yīng)如何調(diào)度該進程。Linux將進程分為實時進程與普通(非實時)進程兩類,分別采用不同的調(diào)度策略。
(2)實時優(yōu)先級(rt_priority):實時進程的優(yōu)先級,標志實時進程優(yōu)先權(quán)的高低,取值范圍為0(最低)~99(最高)。實時進程此項為1~99,非實時進程此項為0。
(3)靜態(tài)優(yōu)先級(static_prio):進程的基本優(yōu)先級。
(4)動態(tài)優(yōu)先級(prio):進程調(diào)度使用的實際優(yōu)先級。它是對靜態(tài)優(yōu)先級的調(diào)整。
(5)時間片(time_slice):進程當前剩余的時間。
2. 進程調(diào)度的機制
1)調(diào)度策略
Linux進程調(diào)度的宗旨是及時地響應(yīng)實時進程,公平地響應(yīng)普通進程。在調(diào)度策略上,實時進程的優(yōu)先級要高于普通進程。因此系統(tǒng)中只要有實時進程就緒,內(nèi)核就會調(diào)度它們運行,直至全部完成。對普通進程,內(nèi)核采用的是均衡的調(diào)度策略,力圖使所有進程都能公平地獲得執(zhí)行機會。
2)調(diào)度程序
Linux的主調(diào)度程序是schedule()函數(shù)。schedule()是調(diào)度的入口,每當需要切換進程時都要調(diào)用這個函數(shù)。schedule()的工作就是選擇下一個要運行的進程,然后切換現(xiàn)場,讓選中的進程運行。選擇進程的工作是通過調(diào)用具體的調(diào)度程序完成的。Linux提供了兩個具體調(diào)度程序,即用于實時進程的RT調(diào)度器和用于普通進程的CFS調(diào)度器。選擇的順序是先RT后CFS,即如果有可調(diào)度的實時進程就調(diào)用RT調(diào)度器,選擇一個實時進程,否則就調(diào)用CFS調(diào)度器,選擇一個普通進程。
3)調(diào)度實體與隊列
調(diào)度程序的操作對象并不是進程描述符本身,而是包含在進程描述符中的稱為調(diào)度實體的結(jié)構(gòu)變量。調(diào)度實體中包含了調(diào)度算法所需的所有信息,它代表進程參與調(diào)度。實時進程的調(diào)度實體是rt,普通進程的調(diào)度實體是se。
調(diào)度程序所使用的最基本的數(shù)據(jù)結(jié)構(gòu)是可執(zhí)行隊列(runqueue),也稱為就緒隊列。
4)調(diào)度方式
調(diào)度的方式分為主動調(diào)度與搶占調(diào)度兩種。主動調(diào)度就是由進程直接調(diào)用schedule()函數(shù),主動放棄CPU;搶占調(diào)度是由內(nèi)核調(diào)用schedule()函數(shù),強制切換進程。搶占調(diào)度又分為周期性搶占與優(yōu)先級搶占。周期性搶占是在每個時鐘節(jié)拍中判斷是否需要切換進程,如果需要就通知內(nèi)核重新調(diào)度。優(yōu)先級搶占是當有高優(yōu)先級的進程就緒時,內(nèi)核將中斷當前進程,調(diào)度更高優(yōu)先級的進程運行。
5)內(nèi)核搶占方式
上述的搶占機制屬于用戶級的搶占,特點是切換操作都是在用戶態(tài)下發(fā)生的。多數(shù)操作系統(tǒng)(包括2.4版內(nèi)核之前的Linux)都只支持這種搶占機制。在這樣的系統(tǒng)中,內(nèi)核代碼會一直運行到完成,其間不允許重新調(diào)度。假如一個中斷處理的過程中又嵌套了其他的中斷處理,則要在全部處理都執(zhí)行完后返回用戶空間時才允許進程調(diào)度,這顯然會延長系統(tǒng)對高優(yōu)先級進程的響應(yīng)時間。
3. 實時進程的調(diào)度
實時進程的調(diào)度原則是嚴格按優(yōu)先級進行調(diào)度,在同一優(yōu)先級上則采用先進先出或輪轉(zhuǎn)法進行調(diào)度。所有的實時進程都由RT調(diào)度器(RealtimeScheduler)進行調(diào)度。
1)實時進程的可執(zhí)行隊列
實時進程的可執(zhí)行隊列采用rt_rq結(jié)構(gòu)描述,其中包括了實時調(diào)度所需的各種信息。
2)實時進程的調(diào)度策略
實時進程的調(diào)度策略包括先進先出法(SCHED_FIFO)、時間片輪轉(zhuǎn)法(SCHED_RR)和最后期限法(SCHED_DEADLINE)。以前兩種最為常用。
(1)先進先出法(First-In,F(xiàn)irst-Out,F(xiàn)IFO):調(diào)度程序依次選擇當前最高優(yōu)先級的FIFO類型的進程,調(diào)度其運行。
(2)輪轉(zhuǎn)法(RoundRobin,RR):RR調(diào)度算法的基本思想是給每個實時進程分配一個時間片,然后按照它們的優(yōu)先級加入到相應(yīng)的優(yōu)先級隊列中。
嚴格地說,這兩種實時都屬于軟實時,也就是統(tǒng)計意義上的實時,并不能提供像實時Linux系統(tǒng)那樣精確的響應(yīng)時間保證。相比之下,F(xiàn)IFO算法更為簡單,但在一些特殊情況下有欠公平。比如,一個很短的進程排在了一個很長的進程之后,它可能要花費很多的時間來等待。因此,F(xiàn)IFO適用于那些每次運行時間較短的實時進程。RR算法在追求響應(yīng)速度的同時還兼顧到公平性,因而適合于那些運行時間較長的實時進程。
3)實時調(diào)度的實施
當一個實時進程就緒時,內(nèi)核將根據(jù)它的優(yōu)先級將其放入相應(yīng)隊列的尾部。如果該進程的優(yōu)先級高于當前進程的優(yōu)先級,內(nèi)核將調(diào)用schedule()函數(shù)進行搶占,否則就排在隊中等待調(diào)度機會。每次調(diào)度時,若schedule()函數(shù)發(fā)現(xiàn)有實時進程就緒,它就會調(diào)用RT調(diào)度器的pick_next_task_rt()函數(shù),選擇下一個要運行的實時進程,令其運行。
4. 普通進程的調(diào)度
普通進程的調(diào)度策略是普通(SCHED_NORMAL)調(diào)度法。在新內(nèi)核中,普通調(diào)度法就是完全公平調(diào)度法CFS。
1)調(diào)度策略的公平性
相比實時進程,普通進程的調(diào)度更加強調(diào)公平性。
2)CFS的調(diào)度原理
CFS調(diào)度法的要點之一是將CPU的使用權(quán)按比例分配給各就緒進程,據(jù)此算出各進程在一個調(diào)度周期內(nèi)應(yīng)運行的時間。分配的依據(jù)是進程的負載權(quán)重(loadweight)。每個進程都有一個負載權(quán)重,這個權(quán)重是根據(jù)進程的靜態(tài)優(yōu)先級計算而來的。
3)CFS的可執(zhí)行隊列
CFS調(diào)度器的可執(zhí)行隊列用cfs_rq結(jié)構(gòu)描述,包含了隊列本身以及隊列的屬性信息和統(tǒng)計數(shù)據(jù),如當前進程數(shù)目、總權(quán)重、當前最小vruntime值等。
當一個進程轉(zhuǎn)入就緒狀態(tài)后,內(nèi)核將其插入紅黑樹中的適當位置;當一個進程被選中運行時,它的節(jié)點就被從樹中摘去。每次出入隊操作都要對紅黑樹進行平衡調(diào)整,并相應(yīng)地更新隊列的統(tǒng)計數(shù)據(jù)。
4)CFS調(diào)度的實施
當一個普通進程就緒時,CFS調(diào)度器根據(jù)它的vruntime值將其插入紅黑樹中。對于睡眠后被喚醒的進程來說,它的vruntime值的增長通常會落后于其他進程,因此它會被插入到前面的位置,得到優(yōu)先調(diào)度的機會。每次調(diào)度時,若沒有實時進程,schedule()函數(shù)就會調(diào)用CFS調(diào)度器的pick_next_task_fair()函數(shù),從紅黑樹中選出下一個要運行進程,令其運行。
4.5.3 Linux系統(tǒng)的進程切換
當schedule()選中一個新的進程后,下一步就是調(diào)用context_switch()函數(shù),完成進程的切換。切換操作包括兩個步驟:一是切換進程的地址空間;二是切換進程的執(zhí)行環(huán)境,包括內(nèi)核棧和CPU硬件環(huán)境。
切換地址空間就是為CPU安裝上新進程的地址空間,使其訪問新的進程映像。
1. 進程的執(zhí)行環(huán)境
進程在運行時,CPU中各寄存器的值就構(gòu)成了進程的執(zhí)行環(huán)境。進程切換時,執(zhí)行環(huán)境也要跟著改變。在切換點上的執(zhí)行環(huán)境的值就稱為進程的現(xiàn)場。調(diào)度程序需要為換下的進程保存完整的現(xiàn)場,這樣當進程再次獲得運行機會時才能夠恢復(fù)原狀繼續(xù)執(zhí)行。
對于不同的體系架構(gòu),CPU寄存器的配置也不同。表4-1列出了x86和x64CPU的主要寄存器的名稱與用途。
具體地說,在x86系統(tǒng)中,進程的執(zhí)行環(huán)境由以下兩部分構(gòu)成:
1)程序相關(guān)的執(zhí)行環(huán)境
與程序執(zhí)行相關(guān)的寄存器包括通用寄存器、段寄存器、指令寄存器和標志寄存器。這些寄存器給出了程序運行所需的指令、數(shù)據(jù)、地址和狀態(tài)等信息,它們的內(nèi)容隨著指令的執(zhí)行而變化。我們稱這部分環(huán)境為程序執(zhí)行環(huán)境。
2)硬件相關(guān)的執(zhí)行環(huán)境
與CPU運行相關(guān)的寄存器包括控制寄存器、調(diào)試寄存器以及一些有關(guān)浮點計算及CPU模式設(shè)定的寄存器。此外還有一些CPU所使用的系統(tǒng)表和數(shù)據(jù)區(qū),如段描述符表GDT和LDT、中斷向量表IDT和任務(wù)段TSS。這些表的地址保存在系統(tǒng)地址寄存器中。我們稱這部分環(huán)境為硬件執(zhí)行環(huán)境。
2. 進程現(xiàn)場的保存與恢復(fù)
1)程序執(zhí)行環(huán)境的切換
程序執(zhí)行環(huán)境的切換發(fā)生在進程的運行模式轉(zhuǎn)換時。當進程因中斷或系統(tǒng)調(diào)用進入核心態(tài)時,它的用戶態(tài)程序現(xiàn)場被保存在內(nèi)核棧中,CPU切換到核心態(tài)的程序執(zhí)行環(huán)境;當返回用戶態(tài)時,內(nèi)核棧中保存的程序現(xiàn)場被恢復(fù)到CPU,進程又回到用戶態(tài)程序執(zhí)行環(huán)境。
2)進程的切換
進程切換發(fā)生在進程重新調(diào)度時。由于調(diào)度程序運行在核心態(tài),所以此時新舊兩進程的程序現(xiàn)場都已在各自的內(nèi)核棧中,進程切換時只要切換內(nèi)核棧和硬件現(xiàn)場即可。硬件現(xiàn)場切換后,在返回用戶態(tài)時程序現(xiàn)場也被恢復(fù),則整個CPU的執(zhí)行環(huán)境都換為新進程的了。
3. 進程的切換過程
如上所述,switch_to()要做的操作是切換內(nèi)核棧和CPU硬件現(xiàn)場。使CPU平穩(wěn)地從一個執(zhí)行環(huán)境過渡到另一個執(zhí)行環(huán)境是個復(fù)雜的過程,這里只簡單說明主要的步驟。
以下是switch_to()執(zhí)行用B換下A的主要操作步驟:
(1)將后面操作要用到的寄存器(eflags、ebp)的值壓入A的內(nèi)核棧中。
(2)將內(nèi)核棧指針esp寄存器的值保存到A的thread中,將B的thread中保存的esp值設(shè)置到esp寄存器中,這樣就完成了內(nèi)核棧的切換。由于CPU是通過內(nèi)核棧中的thread_info來訪問當前進程的描述符task_struct的,所以此時的當前進程已變?yōu)锽了。
(3)將A的thread中的eip值設(shè)為一個特定的“標記1”地址。
(4)將B的thread中的eip值壓入棧中。由于之前B也是被switch_to()換下的,所以這個eip值就是在那次切換的第(3)步設(shè)置的“標記1”地址。
(5)CPU跳轉(zhuǎn)到_switch_to()函數(shù)入口,開始執(zhí)行硬件環(huán)境的切換。
(6)返回指令從棧中彈出返回地址到eip寄存器。
如果B是一個新創(chuàng)建的進程,則建立時在它的thread中設(shè)置的eip值是fork()返回點處的指令地址,這是進程首次運行的起點地址。因此切換操作的第(6)步執(zhí)行的是這個地址處的指令,它跳過了第(6)步中的出棧操作,直接進入schedule()的結(jié)束處理階段,然后恢復(fù)現(xiàn)場返回用戶空間。此處的恢復(fù)現(xiàn)場操作實際上是為B建立起初始運行環(huán)境,用的是父進程創(chuàng)建它時復(fù)制在其內(nèi)核棧中的程序現(xiàn)場。隨后B就開始了它的首次運行。
4.6 進程的互斥與同步
4.6.1 進程間的制約關(guān)系并發(fā)進程彼此間會產(chǎn)生相互制約的關(guān)系。進程之間的制約關(guān)系有兩種方式:一是進程的同步,即相關(guān)進程為協(xié)作完成同一任務(wù)而引起的直接制約關(guān)系;二是進程的互斥,即進程間因競爭系統(tǒng)資源而引起的間接制約關(guān)系。
1. 臨界資源與臨界區(qū)
臨界資源(criticalresource)是一次僅允許一個進程使用的資源。
臨界區(qū)(criticalregion)是程序中訪問臨界資源的程序片段。
在4.1.1節(jié)中描述了一個停車場車位計數(shù)器的例子(見圖4-1)。當A、B兩個進程同時修改計數(shù)器C時就會發(fā)生更新錯誤,因此C是一個臨界資源,而進程A和B中訪問C的程序段就稱為臨界區(qū),如圖4-14所示。圖4-14臨界資源與臨界區(qū)
2. 進程的互斥與同步機制
因共享臨界資源導(dǎo)致錯誤的發(fā)生,其原因在于多個進程訪問該資源的操作穿插進行。要避免這種錯誤,關(guān)鍵是要用某種方式來阻止多個進程同時訪問臨界資源,這就是互斥。
進程的互斥(mutex)就是禁止多個進程同時進入各自的訪問同一臨界資源的臨界區(qū),以保證對臨界資源的排它性使用。
訪問緩沖區(qū)是一個典型的進程同步問題。
3. 互斥與同步的實現(xiàn)方法
實現(xiàn)進程互斥與同步的手段有多種,在現(xiàn)代操作系統(tǒng)中用得較多的有原子操作、互斥鎖和信號量。原子操作是用特定匯編語言實現(xiàn)的操作,它的操作過程受硬件的支持,具有不可分割性。原子操作主要用于實現(xiàn)資源的計數(shù)。互斥鎖機制是通過給資源加/解鎖的方式來實現(xiàn)互斥訪問資源,主要用于數(shù)據(jù)的保護。信號量是一種比互斥鎖更為靈活的機制,主要用于進程的同步。
4.6.2 信號量同步機制
信號量是最早出現(xiàn)的進程同步機制,因其簡捷有效而被廣泛地用來解決各種同步問題。
1. 信號量與P、V操作
信號量(semaphore)是一個整型變量s,它為某個臨界資源而設(shè)置,表示該資源的可用數(shù)目。
信號量是一種特殊的變量,它僅能被兩個標準的操作來訪問和修改。這兩個操作分別稱為P操作和V操作。
2. 用P、V操作實現(xiàn)進程互斥
設(shè)進程A和進程B都要訪問臨界資源C。為實現(xiàn)互斥訪問,需要為臨界資源C設(shè)置一個信號量s,初值設(shè)為1。當進程運行到臨界區(qū)開始處時,先要做P(s)操作,申請資源s。當進程運行到臨界區(qū)結(jié)束處時,要做V(s)操作,釋放資源s。進程A和進程B的執(zhí)行過程如圖4-15所示。圖4-15用P、V操作實現(xiàn)進程的互斥
3. 用P、V操作實現(xiàn)進程同步
設(shè)兩進程為協(xié)作完成某一項工作,需要共享一個緩沖區(qū)。先是一個進程C往緩沖區(qū)中寫數(shù)據(jù),然后另一個進程D從緩沖區(qū)中讀取數(shù)據(jù),如此循環(huán)直至處理完畢。緩沖區(qū)屬于臨界資源,為使這兩個進程能夠協(xié)調(diào)步調(diào),串行地訪問緩沖區(qū),需用P、V操作來同步兩進程。這種工作模式稱為生產(chǎn)者-消費者模式。
同步的方法如下:
將緩沖區(qū)看作兩個臨界資源:一個是可讀緩沖區(qū),也就是“滿”緩沖區(qū);另一個是可寫緩沖區(qū),也就是“空”緩沖區(qū)。分別為它們設(shè)置一個信號量。s1是可讀緩沖區(qū)資源的信號量。s1=1時表示可讀緩沖區(qū)數(shù)為1,s1=0時表示沒有可讀緩沖區(qū);s2是可寫緩沖區(qū)資源的信號量。s2=1時表示可寫緩沖區(qū)數(shù)為1,s2=0時表示沒有可寫的緩沖區(qū)。s1的初值為0,s2的初值為1。
進程C和進程D的執(zhí)行過程如圖4-16所示。圖4-16用P、V操作實現(xiàn)進程的同步
4.6.3 Linux的信號量機制
在Linux系統(tǒng)中存在兩種信號量的實現(xiàn)機制:一種是針對系統(tǒng)的臨界資源設(shè)置的,是由內(nèi)核使用的信號量,另一種是供用戶進程使用的。
4.6.4 死鎖問題
死鎖(deadlock)是指系統(tǒng)中若干個進程相互“無知地”等待對方所占有的資源而無限地處于等待狀態(tài)的一種僵持局面,其現(xiàn)象是若干個進程均停頓不前,且無法自行恢復(fù)。
死鎖的根本原因是系統(tǒng)資源有限,而多個并發(fā)進程因競爭資源而相互制約。相互制約的進程需要彼此等待,在極端情況下,就可能出現(xiàn)死鎖。圖4-17示意了可能引發(fā)死鎖的一種運行情況。圖4-17可能導(dǎo)致死鎖的資源使用方式
分析死鎖的原因,可以歸納出產(chǎn)生死鎖的4個必要條件:
(1)資源的獨占使用:資源由占有者獨占,不允許其他進程同時使用。
(2)資源的非搶占式分配:資源一旦分配就不能被剝奪,直到占用者使用完畢釋放。
(3)對資源的保持和請求:進程因請求資源而被阻塞時,對已經(jīng)占有的資源保持不放。
(4)對資源的循環(huán)等待:每個進程已占用一些資源,而又等待別的進程釋放資源。
解決死鎖的方案就是破壞死鎖產(chǎn)生的必要條件之一,方法有:
(1)預(yù)防:對資源的用法進行適當?shù)南拗啤?/p>
(2)檢測:在系統(tǒng)運行中隨時檢測死鎖的條件,并設(shè)法避開。
(3)恢復(fù):死鎖發(fā)生時,設(shè)法以最小的代價退出死鎖狀態(tài)。
預(yù)防是指采取某種策略,改變資源的分配和控制方式,使死鎖的條件無法產(chǎn)生。但這種做法會導(dǎo)致系統(tǒng)的資源也無法得到充分的利用。檢測是指對資源的使用情況進行監(jiān)視,遇到有可能引發(fā)死鎖的情況就采取措施避開。這種方法需要大量的系統(tǒng)開銷,通常會以降低系統(tǒng)的運行效率為代價。因此,現(xiàn)在的系統(tǒng)大都采取恢復(fù)的方法,就是在死鎖發(fā)生后,檢測死鎖發(fā)生的位置和原因,用外力撤銷一個或幾個進程,或重新分配資源,使系統(tǒng)從死鎖狀態(tài)中恢復(fù)過來。
4.7 進程通信
4.7.1 進程通信的方式進程間的通信有多種方式,大致可以分為信號量、信號、管道、消息和共享內(nèi)存幾類。從通信的功能來分,進程通信方式可以分為低級通信和高級通信兩類。低級通信只是傳遞少量的數(shù)據(jù),用于通知對方某個事件;高級通信則可以用來在進程之間傳遞大量的信息。低級通信方式有信號量和信號,高級通信方式有消息、管道和共享內(nèi)存等。
按通信的同步方式來分,進程通信又分為同步通信與異步通信兩類。同步通信是指通信雙方進程共同參與整個通信過程,步調(diào)協(xié)調(diào)地發(fā)送和接收數(shù)據(jù)。這就像打電話,雙方必須同時在線,同步交談。異步通信則不同,通信雙方的聯(lián)系比較松散,通信的發(fā)送方不必考慮對方的狀態(tài),發(fā)送完就繼續(xù)運行。接收方也不關(guān)心發(fā)送方的狀態(tài),在自己適合的時候接收數(shù)據(jù)。異步通信方式就如同發(fā)送電子郵件,不必關(guān)心對方何時接收。管道和共享內(nèi)存等都屬于同步通信,而信號、消息則屬于異步通信。
Linux系統(tǒng)支持以下幾種IPC機制:
(1)信號量(semaphore):信號量分為內(nèi)核信號量與IPC信號量,分別用于核心態(tài)與用戶態(tài)進程的同步與互斥。
(2)信號(signal):信號是進程間可互相發(fā)送的控制信息,一般只是幾個字節(jié)的數(shù)據(jù),用于通知進程有某個事件發(fā)生。
(3)管道(pipe):管道是連接兩個進程的一個數(shù)據(jù)傳輸通路,一個進程向管道寫數(shù)據(jù),另一個進程從管道讀數(shù)據(jù),實現(xiàn)兩進程之間同步傳遞字節(jié)流。
(4)消息隊列(messagequeue):消息是結(jié)構(gòu)化的數(shù)據(jù),消息隊列是由消息鏈接而成的鏈式隊列。
(5)共享內(nèi)存(shared-memory):共享內(nèi)存通信方式就是在內(nèi)存中開辟一段存儲區(qū),將這個區(qū)映射到多個進程的地址空間中,使得多個進程共享這個內(nèi)存區(qū)。
4.7.2 Linux信號通信原理
信號是來自UNIX系統(tǒng)的最古老的IPC機制之一,用于在進程之間傳遞控制信號。信號屬于低級通信,因簡單有效而得到廣泛使用,任何一個進程都具有接收和處理信號的能力。
1. 信號的概念
信號是一組正整數(shù)常量,進程之間通過傳送信號來通信,通知進程發(fā)生了某事件。
Linux系統(tǒng)定義了32個常規(guī)信號,另外還有32個擴展信號。表4-2列出了常用的信號及其用途和默認處理方式。用kill-l命令可以列出系統(tǒng)的全部可用信號。
2. Linux的信號描述結(jié)構(gòu)
Linux系統(tǒng)用于信號管理的主要數(shù)據(jù)結(jié)構(gòu)是信號描述符signal_struct、信號處理描述符sighand_struct以及信號隊列結(jié)構(gòu)sigpending,描述結(jié)構(gòu)如圖4-18所示。圖4-18?Linux進程的信號描述結(jié)構(gòu)
3. 信號的處理過程
一個信號從出現(xiàn)到處理完畢需要經(jīng)歷兩個階段:一個是信號產(chǎn)生(generation)階段,即形成待處理的信號;另一個是信號交付(deliver)階段,就是將產(chǎn)生的信號交付處理。
1)信號的產(chǎn)生
信號產(chǎn)生的過程可分為發(fā)出、發(fā)送和通知幾個步驟。
2)信號的交付
信號的交付過程包括檢測、提取與處理幾個步驟。
4. 信號的處理方式
信號的處理方式分為以下3種:
(1)忽略(ignore):不做任何處理;
(2)默認(default):調(diào)用內(nèi)核的默認處理函數(shù)來處理信號;
(3)捕獲(catch):執(zhí)行進程自己設(shè)置的信號處理函數(shù)。
5. 信號的應(yīng)用
進程使用kill()等系統(tǒng)調(diào)用來發(fā)信號,控制進程的運行。終端用戶可以用kill命令來達到同樣的目的。kill命令是對kill()系統(tǒng)調(diào)用的命令級封裝,它可以向指定的進程發(fā)信號。對前臺進程還可以用鍵盤按
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 渡槽施工方案
- 排水施工方案
- 液壓玩具模型施工方案
- 場站路基填筑施工方案
- 庭院毛石改造施工方案
- 煙臺冷庫安裝施工方案
- TSHJMRH 0064-2024 在用潤滑油磨損金屬和污染物元素的測定 旋轉(zhuǎn)圓盤電極原子發(fā)射光譜法
- 二零二五年度車展活動展位搭建與品牌宣傳合同
- 二零二五年度超市店長入股合作協(xié)議書
- 2025年度餐廳員工勞動合同保密條款
- 百所名校高一數(shù)學(xué)試卷
- 4.2 同學(xué)相伴 第二課時 課件 2024-2025學(xué)年三年級下冊道德與法治 統(tǒng)編版
- 2025年全球及中國調(diào)頻儲能行業(yè)頭部企業(yè)市場占有率及排名調(diào)研報告
- 第九章-或有事項教學(xué)教材
- 2024年江西青年職業(yè)學(xué)院高職單招職業(yè)適應(yīng)性測試歷年參考題庫含答案解析
- 2025年春新冀教版英語三年級下冊課件 2L2
- 2025年度會計人員繼續(xù)教育會計法律法規(guī)答題活動測試100題答案
- 消防維保年度工作計劃
- 棗莊學(xué)院《電力拖動與自動控制系統(tǒng)》2023-2024學(xué)年第一學(xué)期期末試卷
- 2025中國聯(lián)通廣東省分公司招聘187人高頻重點提升(共500題)附帶答案詳解
- 研學(xué)旅行課程設(shè)計廣西
評論
0/150
提交評論