湯子瀛 作業系統整理[2]——第二章 程序管理
第二章 程序管理
程序的基本概念
程序的三種基本狀態:就緒(Ready)狀態、執行狀態、阻塞狀態;
具有掛起狀態的程序狀態圖
引入掛起狀態的原因:①終端使用者的請求;②父程序請求;③負荷調節的需要;④作業系統的需要。
程序控制塊
1. 程序控制塊的作用:使一個在多道程式環境下不能獨立執行的程式(含資料),成為一個能獨立執行的基本單位,一個能與其它程序併發執行的程序。或者說,OS是根據PCB來對併發執行的程序進行控制和管理的。
2. 程序控制塊中的資訊:
1) 程序識別符號:程序識別符號用於惟一地標識一個程序。一個程序通常有兩種識別符號:
①內部識別符號
②外部識別符號。它由建立者提供,通常是由字母、數字組成,往往是由使用者(程序)在訪問該程序時使用。為了描述程序的家族關係, 還應設定父程序標識及子程序標識。此外,還可設定使用者標識,以指示擁有該程序的使用者。
2) 處理機狀態:處理機狀態資訊主要是由處理機的各種暫存器中的內容組成的。
①通用暫存器,又稱為使用者可視暫存器,它們是使用者程式可以訪問的,用於暫存資訊;
②指令計數器,其中存放了要訪問的下一條指令的地址;
③程式狀態字PSW
④使用者棧指標,指每個使用者程序都有一個或若干個與之相關的系統棧,用於存放過程和系統呼叫引數及呼叫地址。棧指標指向該棧的棧頂。
3) 程序排程資訊:存放一些與程序排程和程序對換有關的資訊,包括:
①程序狀態,指明程序的當前狀態,作為程序排程和對換時的依據;
②程序優先順序,描述程序使用處理機的優先級別的一個整數,優先順序高的程序應優先獲得處理機;
③程序排程所需的其它資訊,它們與所採用的程序排程演算法有關,比如,程序已等待CPU的時間總和、 程序已執行的時間總和等;
④事件,是指程序由執行狀態轉變為阻塞狀態所等待發生的事件,即阻塞原因。
4) 程序控制資訊:程序控制資訊包括:
①程式和資料的地址,是指程序的程式和資料所在的記憶體或外存地(首)址,以便再排程到該程序執行時,能從PCB中找到其程式和資料;
②程序同步和通訊機制,指實現程序同步和程序通訊時必需的機制,如訊息佇列指標、訊號量等,它們可能全部或部分地放在PCB中;
③資源清單,是一張列出了除CPU以外的、程序所需的全部資源及已分配到該程序的資源的清單;
④連結指標,它給出了本程序(PCB)所在佇列中的下一個程序的PCB的首地址。
程序控制
程序的建立
引起建立程序的事件:使用者登入、作業排程、提供服務、應用請求;
程序的建立(Creation of Progress):
(1)申請空白PCB。
(2)為新程序分配資源。
(3)初始化程序控制塊。
(4)將新程序插入就緒佇列,如果程序就緒佇列能夠接納新程序, 便將新程序插入就緒佇列。
程序的終止
引起程序終止(Termination of Process)的事件:正常結束、異常結束、外界干預;
程序的終止過程:
(1)根據被終止程序的識別符號,從PCB集合中檢索出該程序的PCB,從中讀出該程序的狀態。
(2)若被終止程序正處於執行狀態,應立即終止該程序的執行,並置排程標誌為真,用於指示該程序被終止後應重新進行排程。
(3)若該程序還有子孫程序,還應將其所有子孫程序予以終止,以防他們成為不可控的程序。
(4)將被終止程序所擁有的全部資源,或者歸還給其父程序,或者歸還給系統。
(5)將被終止程序(它的PCB)從所在佇列(或連結串列)中移出,等待其他程式來蒐集資訊。
程序的阻塞與喚醒
引起程序阻塞和喚醒的事件: 請求系統服務、啟動某種操作、新資料尚未到達、無新工作可做;
程序阻塞過程:
(1)正在執行的程序,當發現上述某事件時,由於無法繼續執行,於是程序便通過呼叫阻塞原語block把自己阻塞。可見,程序的阻塞是程序自身的一種主動行為。
(2)進入block過程後,由於此時該程序還處於執行狀態,所以應先立即停止執行,把程序控制塊中的現行狀態由“執行”改為阻塞,並將PCB插入阻塞佇列。如果系統中設定了因不同事件而阻塞的多個阻塞佇列,則應將本程序插入到具有相同事件的阻塞(等待)佇列。
(3)最後,轉排程程式進行重新排程,將處理機分配給另一就緒程序,並進行切換,亦即,保留被阻塞程序的處理機狀態(在PCB中),再按新程序的PCB中的處理機狀態設定CPU的環境。
程序喚醒過程:當被阻塞程序所期待的事件出現時,如I/O完成或其所期待的資料已經到達,則由有關程序(比如,用完並釋放了該I/O裝置的程序)呼叫喚醒原語wakeup( ),將等待該事件的程序喚醒。喚醒原語執行的過程是:首先把被阻塞的程序從等待該事件的阻塞佇列中移出,將其PCB中的現行狀態由阻塞改為就緒,然後再將該PCB插入到就緒佇列中
程序的掛起與啟用
程序的掛起:當出現了引起程序掛起的事件時,比如,使用者程序請求將自己掛起,或父程序請求將自己的某個子程序掛起,系統將利用掛起原語suspend( )將指定程序或處於阻塞狀態的程序掛起。掛起原語的執行過程是:首先檢查被掛起程序的狀態,若處於活動就緒狀態,便將其改為靜止就緒;對於活動阻塞狀態的程序,則將之改為靜止阻塞。為了方便使用者或父程序考查該程序的執行情況而把該程序的PCB複製到某指定的記憶體區域。最後,若被掛起的程序正在執行,則轉向排程程式重新排程。
程序的啟用過程:當發生啟用程序的事件時,例如,父程序或使用者程序請求啟用指定程序,若該程序駐留在外存而記憶體中已有足夠的空間時,則可將在外存上處於靜止就緒狀態的程序換入記憶體。這時,系統將利用啟用原語active( )將指定程序啟用。啟用原語先將程序從外存調入記憶體,檢查該程序的現行狀態,若是靜止就緒,便將之改為活動就緒;若為靜止阻塞便將之改為活動阻塞。假如採用的是搶佔排程策略,則每當有新程序進入就緒佇列時,應檢查是否要進行重新排程,即由排程程式將被啟用程序與當前程序進行優先順序的比較,如果被啟用程序的優先順序更低,就不必重新排程;否則,立即剝奪當前程序的執行,把處理機分配給剛被啟用的程序。
程序同步
程序同步的基本概念
可把一個訪問臨界資源的迴圈程序描述如下:
repeat
entry section
critical section;
exit section
remainder section;
until false;
同步機制應遵循的規則:空閒讓進、忙則等待、有限等待、讓權等待。
訊號量機制
1.整型訊號量
除初始化外,僅能通過兩個標準的原子操作(Atomic Operation) wait(S)和signal(S)來訪問。這兩個操作一直被分別稱為P、V操作。 wait和signal操作可描述為:
wait(S): while S≤0 do no-op
S∶=S-1;
signal(S): S∶=S+1;
在整型訊號量機制中的wait操作,只要是訊號量S≤0, 就會不斷地測試。因此,該機制並未遵循“讓權等待”的準則, 而是使程序處於“忙等”的狀態。
2.記錄型訊號量
記錄型訊號量機制,是一種不存在“忙等”現象的程序同步機制。但在採取了“讓權等待”的策略後,又會出現多個程序等待訪問同一臨界資源的情況。為此,在訊號量機制中,除了需要一個用於代表資源數目的整型變數value外,還應增加一個程序連結串列L,用於連結上述的所有等待程序。記錄型訊號量是由於它採用了記錄型的資料結構而得名的。它所包含的上述兩個資料項可描述為:
type semaphore=record
value:integer;
L:list of process;
end
相應地,wait(S)和signal(S)操作可描述為:
procedure wait(S)
var S: semaphore;
begin
S.value∶ =S.value-1;
if S.value<0 then block(S,L)
end
procedure signal(S)
var S: semaphore;
begin
S.value∶ =S.value+1;
if S.value≤0 then wakeup(S,L);
end
S.value的初值表示系統中某類資源的數目, 因而又稱為資源訊號量,對它的每次wait操作,意味著程序請求一個單位的該類資源,因此描述為S.value∶=S.value-1; 當S.value<0時,表示該類資源已分配完畢,因此程序應呼叫block原語,進行自我阻塞,放棄處理機,並插入到訊號量連結串列S.L中。可見,該機制遵循了“讓權等待”準則。此時S.value的絕對值表示在該訊號量連結串列中已阻塞程序的數目。 對訊號量的每次signal操作,表示執行程序釋放一個單位資源,故S.value∶=S.value+1操作表示資源數目加1。 若加1後仍是S.value≤0,則表示在該訊號量連結串列中,仍有等待該資源的程序被阻塞,故還應呼叫wakeup原語,將S.L連結串列中的第一個等待程序喚醒。如果S.value的初值為1,表示只允許一個程序訪問臨界資源,此時的訊號量轉化為互斥訊號量。
3.AND型訊號量
將程序在整個執行過程中需要的所有資源,一次性全部地分配給程序,待程序使用完後再一起釋放。只要尚有一個資源未能分配給程序,其它所有可能為之分配的資源,也不分配給他。亦即,對若干個臨界資源的分配,採取原子操作方式:要麼全部分配到程序,要麼一個也不分配。由死鎖理論可知,這樣就可避免上述死鎖情況的發生。為此,在wait操作中,增加了一個“AND”條件,故稱為AND同步,或稱為同時wait操作, 即Swait(Simultaneous wait)定義如下:
Swait(S1, S2, …, Sn)
if Si≥1 and … and Sn≥1 then
for i∶ =1 to n do
Si∶=Si-1;
endfor
else
place the process in the waiting queue associated with the first Si found with Si<1, and set the program count of this process to the beginning of Swait operation
endif
Ssignal(S1, S2, …, Sn)
for i∶ =1 to n do
Si=Si+1;
Remove all the process waiting in the queue associated with Si into the ready queue.
endfor;
4.訊號量集
Swait(S1, t1, d1, …, Sn, tn, dn)
if Si≥t1 and … and Sn≥tn then
for i∶=1 to n do
Si∶=Si-di;
endfor
else
Place the executing process in the waiting queue of the first Si with Si<ti and set its program counter to the beginning of the Swait Operation.
endif
signal(S1, d1, …, Sn, dn)
for i∶=1 to n do
Si ∶=Si+di;
Remove all the process waiting in the queue associated with Si into the ready queue
endfor;
一般“訊號量集”的幾種特殊情況:
(1) Swait(S, d, d)。 此時在訊號量集中只有一個訊號量S, 但允許它每次申請d個資源,當現有資源數少於d時,不予分配。
(2) Swait(S, 1, 1)。 此時的訊號量集已蛻化為一般的記錄型訊號量(S>1時)或互斥訊號量(S=1時)。(3) Swait(S, 1, 0)。這是一種很特殊且很有用的訊號量操作。當S≥1時,允許多個程序進入某特定區;當S變為0後,將阻止任何程序進入特定區。換言之,它相當於一個可控開關。
訊號量的應用
1. 利用訊號量實現程序互斥
利用訊號量實現程序互斥的程序可描述如下:
Var mutex:semaphore∶ =1;
begin
parbegin
process 1: begin
repeat
wait(mutex);
critical section
signal(mutex);
remainder seetion
until false;
end
process 2: begin
repeat
wait(mutex);
critical section
signal(mutex);
remainder section
until false;
end
parend
2. 利用訊號量實現前趨關係
Var a,b,c,d,e,f,g; semaphore∶=0,0,0,0,0,0,0;
begin
parbegin
begin S1; signal(a); signal(b); end;
begin wait(a); S2; signal(c); signal(d); end;
begin wait(b); S3; signal(e); end;
begin wait(c); S4; signal(f); end;
begin wait(d); S5; signal(g); end;
begin wait(e); wait(f); wait(g); S6; end;
parend
end
管程機制
一個管程定義了一個數據結構和能為併發程序所執行(在該資料結構上)的一組操作,這組操作能同步程序和改變管程中的資料;
管程由三部分組成:① 區域性於管程的共享變數說明;② 對該資料結構進行操作的一組過程;③ 對區域性於管程的資料設定初始值的語句。此外,還須為管程賦予一個名字。
程序通訊
程序通訊的型別
1. 共享儲存器系統(Shared-Memory System)
(1)基於共享資料結構的通訊方式。
(2)基於共享儲存區的通訊方式。
2. 訊息傳遞系統(Message passing system)
程序間的資料交換,是以格式化的訊息(message)為單位的;在計算機網路中,又把message稱為報文。
3. 管道(Pipe)通訊
“管道”,是指用於連線一個讀程序和一個寫程序以實現他們之間通訊的一個共享檔案,又名pipe檔案。向管道(共享檔案)提供輸入的傳送程序(即寫程序), 以字元流形式將大量的資料送入管道;而接受管道輸出的接收程序(即讀程序),則從管道中接收(讀)資料。
為了協調雙方的通訊,管道機制必須提供以下三方面的協調能力:
① 互斥,即當一個程序正在對pipe執行讀/寫操作時,其它(另一)程序必須等待。
② 同步,指當寫(輸入)程序把一定數量(如4 KB)的資料寫入pipe,便去睡眠等待, 直到讀(輸出)程序取走資料後,再把他喚醒。當讀程序讀一空pipe時,也應睡眠等待,直至寫程序將資料寫入管道後,才將之喚醒。
③ 確定對方是否存在,只有確定了對方已存在時,才能進行通訊。
訊息傳遞通訊的實現方法
1. 直接通訊方式
指傳送程序利用OS所提供的傳送命令,直接把訊息傳送給目標程序。此時,要求傳送程序和接收程序都以顯式方式提供對方的識別符號。通常,系統提供下述兩條通訊命令(原語):
Send(Receiver, message); 傳送一個訊息給接收程序;
Receive(Sender, message); 接收Sender發來的訊息;
在某些情況下,接收程序可與多個傳送程序通訊,因此,它不可能事先指定傳送程序。例如,用於提供列印服務的程序,它可以接收來自任何一個程序的“列印請求”訊息。對於這樣的應用,在接收程序接收訊息的原語中的源程序引數,是完成通訊後的返回值,接收原語可表示為:
Receive (id, message);
2. 間接通訊方式
(1) 信箱的建立和撤消。程序可利用信箱建立原語來建立一個新信箱。建立者程序應給出信箱名字、信箱屬性(公用、私用或共享);對於共享信箱, 還應給出共享者的名字。當程序不再需要讀信箱時,可用信箱撤消原語將之撤消。
(2) 訊息的傳送和接收。當程序之間要利用信箱進行通訊時,必須使用共享信箱,並利用系統提供的下述通訊原語進行通訊。
Send(mailbox, message); 將一個訊息傳送到指定信箱;
Receive(mailbox, message); 從指定信箱中接收一個訊息;
信箱可由作業系統建立,也可由使用者程序建立,建立者是信箱的擁有者。
訊息緩衝佇列通訊機制
1. 訊息緩衝佇列通訊機制中的資料結構
(1) 訊息緩衝區。在訊息緩衝佇列通訊方式中,主要利用的資料結構是訊息緩衝區。它可描述如下:
type message buffer=record
sender; 傳送者程序識別符號
size; 訊息長度
text; 訊息正文
next; 指向下一個訊息緩衝區的指標
end
(2) PCB中有關通訊的資料項。在利用訊息緩衝佇列通訊機制時,在設定訊息緩衝佇列的同時,還應增加用於對訊息佇列進行操作和實現同步的訊號量,並將它們置入程序的PCB中。在PCB中應增加的資料項可描述如下:
type processcontrol block=record
…
mq; 訊息佇列隊首指標
mutex; 訊息佇列互斥訊號量
sm; 訊息佇列資源訊號量
…
end
2. 傳送原語
傳送程序在利用傳送原語傳送訊息之前,應先在自己的記憶體空間,設定一發送區a,見圖 2 - 12 所示,把待發送的訊息正文、傳送程序識別符號、訊息長度等資訊填入其中,然後呼叫傳送原語,把訊息傳送給目標(接收)程序。傳送原語首先根據傳送區a中所設定的訊息長度a.size來申請一緩衝區i,接著,把傳送區a中的資訊複製到緩衝區i中。為了能將i掛在接收程序的訊息佇列mq上,應先獲得接收程序的內部識別符號j,然後將i掛在j.mq上。由於該佇列屬於臨界資源, 故在執行insert操作的前後,都要執行wait和signal操作。
procedure send(receiver, a)
begin
getbuf(a.size,i); 根據a.size申請緩衝區;
i.sender∶ =a.sender; 將傳送區a中的資訊複製到訊息緩衝區之中;
i.size∶ =a.size;
i.text∶ =a.text;
i.next∶ =0;
getid(PCB set, receiver.j); 獲得接收程序內部識別符號;
wait(j.mutex);
insert(j.mq, i); 將訊息緩衝區插入訊息佇列;
signal(j.mutex);
signal(j.sm);
end
3. 接收原語
接收原語描述如下:
procedure receive(b)
begin
j∶ =internal name; j為接收程序內部的識別符號;
wait(j.sm);
wait(j.mutex);
remove(j.mq, i); 將訊息佇列中第一個訊息移出;
signal(j.mutex);
b.sender∶ =i.sender; 將訊息緩衝區i中的資訊複製到接收區b;
b.size∶ =i.size;
b.text∶ =i.text;
end
執行緒
執行緒的屬性
①輕型實體。
②獨立排程和分派的基本單位。
③可併發執行。
④共享程序資源。
執行緒的狀態
(1) 狀態引數
① 暫存器狀態,它包括程式計數器PC和堆疊指標中的內容;
② 堆疊, 在堆疊中通常儲存有區域性變數和返回地址;
③ 執行緒執行狀態,用於描述執行緒正處於何種執行狀態;
④ 優先順序,描述執行緒執行的優先程度;
⑤ 執行緒專有儲存器,用於儲存執行緒自己的區域性變數拷貝;
⑥ 訊號遮蔽,即對某些訊號加以遮蔽。
(2) 執行緒執行狀態。
如同傳統的程序一樣,在各執行緒之間也存在著共享資源和相互合作的制約關係,致使執行緒在執行時也具有間斷性。相應地,執行緒在執行時,也具有下述三種基本狀態:
① 執行狀態,表示執行緒正獲得處理機而執行;
② 就緒狀態, 指執行緒已具備了各種執行條件,一旦獲得CPU便可執行的狀態;
③ 阻塞狀態,指執行緒在執行中因某事件而受阻,處於暫停執行時的狀態。
執行緒的建立和終止
在多執行緒OS環境下,應用程式在啟動時,通常僅有一個執行緒在執行,該執行緒被人們稱為“初始化執行緒”。它可根據需要再去建立若干個執行緒。在建立新執行緒時,需要利用一個執行緒建立函式(或系統呼叫),並提供相應的引數,如指向執行緒主程式的入口指標、堆疊的大小,以及用於排程的優先順序等。線上程建立函式執行完後,將返回一個執行緒識別符號供以後使用。
終止執行緒的方式有兩種:一種是線上程完成了自己的工作後自願退出;另一種是執行緒在執行中出現錯誤或由於某種原因而被其它執行緒強行終止。
多執行緒OS中的程序
在多執行緒OS中,程序是作為擁有系統資源的基本單位,通常的程序都包含多個執行緒併為它們提供資源,但此時的程序就不再作為一個執行的實體。多執行緒OS中的程序有以下屬性:
(1) 作為系統資源分配的單位。
(2) 可包括多個執行緒。
(3) 程序不是一個可執行的實體。
執行緒間的同步和通訊
互斥鎖(mutex)
互斥鎖是一種比較簡單的、用於實現程序間對資源互斥訪問的機制。由於操作互斥鎖的時間和空間開鎖都較低, 因而較適合於高頻度使用的關鍵共享資料和程式段。
互斥鎖可以有兩種狀態, 即開鎖(unlock)和關鎖(lock)狀態。 相應地,可用兩條命令(函式)對互斥鎖進行操作。其中的關鎖lock操作用於將mutex關上,開鎖操作unlock則用於開啟mutex。
條件變數
每一個條件變數通常都與一個互斥鎖一起使用,亦即,在建立一個互斥鎖時便聯絡著一個條件變數。單純的互斥鎖用於短期鎖定,主要是用來保證對臨界區的互斥進入。而條件變數則用於執行緒的長期等待,直至所等待的資源成為可用的。
執行緒首先對mutex執行關鎖操作,若成功便進入臨界區,然後查詢用於描述資源狀態的資料結構,以瞭解資源的情況。只要發現所需資源R正處於忙碌狀態,執行緒便轉為等待狀態, 並對mutex執行開鎖操作後,等待該資源被釋放;若資源處於空閒狀態,表明執行緒可以使用該資源,於是將該資源設定為忙碌狀態,再對mutex執行開鎖操作。
下面給出了對上述資源的申請(左半部分)和釋放(右半部分)操作的描述。
Lock mutex Lock mutex
check data structures; mark resource as free;
while(resource busy); unlock mutex;
wait(condition variable); wakeup(condition variable);
mark resource as busy;
unlock mutex;
訊號量機制
(1) 私用訊號量(private samephore)。
當某執行緒需利用訊號量來實現同一程序中各執行緒之間的同步時,可呼叫建立訊號量的命令來建立一私用訊號量,其資料結構是存放在應用程式的地址空間中。私用訊號量屬於特定的程序所有,OS並不知道私用訊號量的存在,因此,一旦發生私用訊號量的佔用者異常結束或正常結束,但並未釋放該訊號量所佔有空間的情況時,系統將無法使它恢復為0(空), 也不能將它傳送給下一個請求它的執行緒。
(2) 公用訊號量(public semaphort)。
公用訊號量是為實現不同程序間或不同程序中各執行緒之間的同步而設定的。由於它有著一個公開的名字供所有的程序使用,故而把它稱為公用訊號量。其資料結構是存放在受保護的系統儲存區中,由OS為它分配空間並進行管理,故也稱為系統訊號量。如果訊號量的佔有者在結束時未釋放該公用訊號量,則OS會自動將該訊號量空間回收,並通知下一程序。可見,公用訊號量是一種比較安全的同步機制。
核心支援執行緒和使用者級執行緒
1. 核心支援執行緒
是在核心的支援下執行的,即無論是使用者程序中的執行緒,還是系統程序中的執行緒,他們的建立、撤消和切換等,也是依靠核心實現的。此外,在核心空間還為每一個核心支援執行緒設定了一個執行緒控制塊,核心是根據該控制塊而感知某執行緒的存在的,並對其加以控制。
2. 使用者級執行緒
使用者級執行緒僅存在於使用者空間中。對於這種執行緒的建立、 撤消、執行緒之間的同步與通訊等功能,都無須利用系統呼叫來實現。對於使用者級執行緒的切換,通常是發生在一個應用程序的諸多執行緒之間,這時,也同樣無須核心的支援。由於切換的規則遠比程序排程和切換的規則簡單,因而使執行緒的切換速度特別快。可見,這種執行緒是與核心無關的。