title: 計算機操作系統-第二章
date: 2019-09-05 21:42:21
mathjax: true
categories:
- operating sysytem
tags: - operating system
文章目錄
進程與線程
-
定義:一個正在執行程序的實例(一個具有一定獨立功能的程序在一個數據集合上的一次動態執行過程),在進程模型中,計算機上所有可以運行的軟件,通常也包括操作系統,都會被組織成若干個進程
-
進程和程序的關係:
- 程序是產生進程的基礎
- 程序的每次運行構成不同的進程
- 進程是程序功能的體現
- 通過多次執行,一個程序可以對應多個進程,通過調用關係,一個進程可以包含多個程序
-
區別:
- 進程是動態的,程序是靜態的,程序是有序代碼的集合,進程是程序的執行,進程有核心態/用戶態
- 進程是暫時的,程序是永久的,進程是一個狀態變化的過程,程序可以長久保存
- 進程和程序的組成不同,進程的組成包括程序、數據和進程控制塊(PCB 進程狀態信息)
-
程序的順序執行:
- 順序性,嚴格按照順序執行;
- 封閉性,封閉環境下運行,獨佔全集資源,結果不受外界干擾;
- 可再現性,程序重複執行,得到相同結果。
-
程序併發執行
- 間斷性,相互合作、相互制約,“執行——暫停——執行”;
- 失去封閉性,系統資源共享,狀態也隨之改變;
- 不可再現性,失去封閉性->失去可再現性。
-
特徵
- 動態性,最基本特徵,進程的實質是進程實體的執行過程,“由創建產生,由調度執行,由撤銷消亡”,進程實體有一定的生命週期,而程序是靜態的,只是一組有序指令的合集;
- 併發行,多個進程實體同存於內存中,能在一段時間內同時運行。程序(沒有建立 PCB) 是不能參與併發執行的,走走停停;
- 獨立性,進程實體是一個能獨立運行、獨立獲得資源和獨立接受調度的基本單位;
- 異步性,異步方式,即各自獨立的、不可預知的速度向前推進。
- 結構性,PCB 描述
-
三個基本狀態:
- 就緒 (Ready) 狀態,進程已分配到除 CPU 外的所有必要資源,只要獲得 CPU 就執行。若系統中有多個就緒狀態的進程,砸按一定的策略排成一個隊列——就緒隊列;
- 執行 (Running)狀態,進程已獲得 CPU,正在執行,單處理機環境,最多一個運行;
- 阻塞 (Block)狀態, 等待態 ,執行的進程由於某事件暫時無法繼續執行。此時引起進程調度。也有阻塞隊列的說法,也稱等待狀態或封鎖狀態。
- 創建狀態和終止狀態
- 創建狀態:比如系統尚無足夠的內存是進程無法裝入其中,此時創建工作尚未完成,進程不能被調度運行
- 終止狀態:當一個進程到達了自然結束點,或者是出現了無法克服的錯誤或者是被操作系統所終結,或者是被其他有終結權限的進程所終結,將進入終結狀態
掛起操作和進程狀態的轉換
- 引入掛起原語操作後三個進程狀態的轉換(suspend和active)
- 活動就緒 -> 靜止就緒
- 活動阻塞 -> 靜止阻塞
- 靜止就緒 -> 活動就緒
- 靜止阻塞 -> 活動阻塞
- 引入掛起操作後5個進程狀態的轉換
- NULL -> 創建
- 創建 -> 活動就緒
- 創建 -> 靜止就緒
- 執行 -> 終止
進程管理中的數據結構
-
進程控制塊(pcb)的作用
- 作爲獨立運行基本單位的標誌(進程的唯一標誌)
- 實現間斷性運行方式
- 提供進程管理所需要的信息
- 提高進程調度所需要的信息
- 實現和其他進程的同步和通信
- 總結:其實就是 地位 & 意義、進程併發、進程管理、進程調度、進程同步、進程通信。爲這些操作提供信息
-
進程控制塊中的信息
-
進程標識符:唯一地標識一個進程,一個進程通常兩種標識符:
外部標識符:方便用戶(進程)對進程的訪問
內部標識符:方便os對進程的使用
-
處理機狀態:處理機的上下文,由各種寄存器中的內容組成
-
進程調度信息:進程狀態、進程優先級、進程調度所需的其他信息、事件
-
進程控制信息:程序和數據的地址、進程同步和通信機制、資源清單、鏈接指針
-
進程控制
-
處理機執行狀態
- 系統態:又稱爲管態,也稱爲內核態,它具有較高的特權,能執行一切指令,訪問所有寄存器和存儲區,傳統的os都在系統態運行
- 用戶態,又稱爲目態,它具有較低特權的執行狀態,僅能執行規定的指令,訪問指定的寄存器和存儲區
-
支撐功能
- 中斷處理``,最基本,OS 活動的基礎
- 時鐘管理,時間控制
- 原語操作,``原語(Primitive),是由若干條指令組成的,用於完成一定功能的一個過程,它是”原子操作“區別於一般過程。”原子操作“指,一個操作中的所有動作要麼全做
-
資源管理功能
- 進程管理
- 存儲器管理
- 設備管理
-
進程的創建
- 進程的層次結構:OS 允許一個進程創建另一個進程,父進程和子進程
2. 引起創建進程的事件:用戶登錄、作業調度、提供服務、應用請求
3. 進程的創建:os調用進程原語創建一個新進程
進程的終止
- 引起終止是事件
- 正常結束:表示進程的任務已經完成,準備退出運行
- 異常結束:進程在運行時候發生了某種異常事件,是程序無法繼續運行
- 外界干預:是指進程應外界的請求而終止運行
- 如果系統中發生了要求終止進程的某事件,OS便調用進程終止原語,按下述過程去終止指定的進程:
- 根據被終止進程的標識符,從PCB集合中檢索出該進程的PCB,從中讀出該進程的狀態;
- 若被終止進程正處於執行狀態,應立即終止該進程的執行,並置調度標誌爲真,用於指示該進程被終止後應重新進行調度;
- 若該進程還有子孫進程,還應將其所有子孫進程也都予以終止,以防它們成爲不可控的進程;
- 將被終止進程所擁有的全部資源或者歸還給其父進程,或者歸還給系統;
- 將被終止進程(PCB)從所在隊列(或鏈表)中移出,等待其它程序來蒐集信息
進程的阻塞和喚醒
-
引起進程阻塞和喚醒的事件:向系統請求共享資源失敗、等待某種操作的完成、新數據尚未到達、等待新任務的到達
-
進程阻塞過程:正在執行的進程,如果發生了上述事件,進程便通過調用阻塞原語block將自己阻塞,阻塞是進程的一種主動行爲
-
進程喚醒過程:當被阻塞進程所期待的事情發生時候,則由有關進程(比如提供數據的進程)嗲用wakeup原語,將等待該事件的喚醒
- 將被阻塞的進程從等待該事件的阻塞隊列中溢出,將其PCB中的狀態從阻塞改爲就緒
- 將PCB插入到就緒隊列中
進程的掛起和喚醒
-
進程掛起
當出現了引起進程掛起的事件時,系統將利用掛起原語 suspend 將指定進程掛起。掛起原語的執行過程是:
-
檢查被掛起進程的狀態:若正處於活動就緒狀態,便將其改爲靜止就緒;
-
對於活動阻塞狀態的進程,則將其改爲靜止阻塞;
-
爲了方便用戶或父進程考查該進程的運行情況,而把該進程的 PCB複製到某指定的內存區域;
-
最後,如被掛起的進程正在執行,則轉調度程序重新調度
-
-
進程激活
當發生激活過程的事件時,若進程駐留在外存而內存中已有足夠的空間,則可將在外存上處於靜止就緒狀態的進程換入內存。這時,系統將利用激活原語 active 將指定進程激活。
-
先將進程從外存調入內存,檢查該進程的現行狀態:若是靜止就緒,便將其改爲活動就緒;若爲靜止阻塞,便將其改爲活動阻塞。
-
假如採用的是搶佔調度策略,則每當有新進程進入就緒隊列時,應檢查是否要進行重新調度,即由調度程序將被激活進程與當前進程進行優先級的比較,如果被激活進程的優先級更低,就不必重新調度;否則,立即剝奪當前進程的運行,把處理機分配給剛被激活的進程。
-
進程同步
進程同步的基本概念
-
主要任務是對多個相關進程在執行次序進行協調,使併發執行的諸進程之間能按照一定的規則(或時序)共享系統資源,並能很好合作,從而使程序的執行具有可再現性
-
兩種制約關係
- 間接相互制約關係:多個進程併發執行,由於共享系統資源,形成的制約關係
- 直接相互制約關係:進程的相互合作導致直接制約關係
-
臨界區(Critical section)
不論是硬件臨界資源還是軟件臨界資源,多個進程必須互斥地對它進行訪問。人們把在每個進程中訪問臨界資源的那段代碼稱爲臨界區
-
同步機制應遵循的規則
- 空閒讓進
- 忙則等待
- 有限等待
- 讓權等待
硬件同步機制
- 關中斷:關中斷是實現互斥的最簡單的方法之一。在進入鎖測試之前關閉中斷,直到完成鎖測試並上鎖之後才能打開中斷
- 濫用關中斷權力可能導致嚴重後果
- 關終端時間過長,會影響系統效率,限制了處理器交叉執行程序的能力
- 關中斷方法不適用於多cpu系統
- 利用 Test-and-Set 指令實現互斥
- 利用 Swap 指令實現進程互斥
信號量機制
-
整型信號量:最初信號量 S 是一個整型變量,除初始化外,對信號量僅能執行兩個原子操作,即 wait(S) 和 signal(S),這兩個原子操作以前多稱爲 P、V 操作。並未遵循“讓權等待”的準則
wait(S) { while (S<=0); // do no-loop S--; } singal(S) { S++; }
-
記錄型信號量:採取了“讓權等待”的策略後,出現多個進程等待訪問同一臨界資源的情況。爲此,在信號量機制中,除了需要一個用於代表資源數目的整型變量 value 外,還應增加一個進程鏈表指針list,用於鏈接上述的所有等待進程
typedef strcut { int value; struct process_control_block *list; }semaphore; wait(semaphore *S) { S->value--; if(S->value<=0) block(S->list); } singal(semaphore *S) { S->value++; if(S->value<=0) wakeup(S->list); }
S->value 的初值表示系統中某類資源的數目,又稱資源信號量,每次 wait 操作,意味着進程請求一個單位的該類資源,使系統中可供分類的該類資源數減少一個,所以是 S->value–;當 S->value<0 時,表示該類資源已分配完畢,則調用 block 進行自我阻塞,插入 list;這就是“讓權等待”準則。此時,S->value 的絕對值表示在該信號量鏈表中已阻塞進程的數目,對信號量的每次signal操作表示執行進程釋放一個單位資源,使系統紅可供分配的該類資源數增加一個,s->value++操作表示資源數目加1.若加1後面s->value<=0,則表示在該信號量的鏈表中人有等待該資源的進程被阻塞,還應調wakeup原語,將s->list鏈表中的第一個等待進程喚醒,如果s->value的處置爲,表示i只允許一個進程訪問臨界資源,此時的信號量轉化爲互斥信號量,進程互斥
-
AND 型信號量:在有些應用場合,是一個進程往往需要獲得兩個或更多的共享資源後方能執行其任務。假定現有兩個進程 A 和 B,它們都要求訪問共享數據 D 和 E。則設置兩個互斥信號量 Dmutex 和Emutex,初值爲 1
AND 同步機制基本思想:將進程中整個運行過程中需要的所有資源,一次性全部地分配給進程,待進程使用完後再一起釋放。在一個原語中,對多個臨界資源的分配採用原子操作方式,要麼全部分配給它,要麼一個都不分配。稱爲 Swait(Simultaneous Wait)
Swait(S1,S2,....Sn) { while(TRUE) { if(S1>=1 && S2>=1 && ....&& Sn>=1){ for (i=1;i<=n;i++) Si--; break; } else{ place the process in the waiting queue associaed with the first Si found with Si<1, and set the program count of this process to the beginning of Swait operation } } } Ssignal(S1,S2,....Sn) { while(TRUE) { for (i=1;i<=n;i++) { Si++; remove all the process waiting in the queue associated with Si into the ready queue } } }
-
信號量集
對 AND 信號量機制加以擴充,對進程所申請的所有資源以及每類資源不同的資源需求量,在一次 P、V 原語操作中完成申請或釋放。進程對信號量 的測試值由 1 變爲該資源的分配下限值 ,即要求 ,否則不予分配。一旦允許分配,進程對該資源的需求值爲 ,表示資源佔用量,進行 的命令。對應的的 Swait 和 Ssingal 格式爲:
Swait();
Ssingle();
一般“信號量集”有下面幾種特殊情況:
-
Swait(S, d, d)。表示每次申請 d 個資源,當少於 d 個時,便不分配;
-
Swait(S, 1, 1)
-
S>1。蛻化爲記錄型信號量;
-
S=1。蛻化爲互斥信號量;
-
-
Swait(S, 1, 0) 作爲一個可控開關
-
當 S≥1 時,允許任何進程進入臨界區;
-
當 S=0 時,禁止任何進程進入臨界區
-
-
信號量集的應用
-
利用信號量實現互斥
設 mutex 爲互斥信號量,初值爲 1。
-
mutex = 1,兩個進程皆未進入需要互斥的臨界區;
-
mutex = 0,一個進程進入臨界區運行,另外一個必須等待,掛入阻塞隊列;
-
mutex = -1,一個進程正在臨界區運行,另一個進程因等待而阻塞在信號量隊列中,需要被當前以在臨界區運行的進程退出時喚醒。
-
-
在利用信號量機制實現進程互斥時候應該注意,wait(mutex)和signal(muex)必須成對出現
- 缺少wait(mutex)將會導致系統混亂,不能保證對臨界資源的戶次訪問
- 確實signal(mutex)將會使臨界資源永遠不被釋放,從而使因等待該資源而阻塞的進程不能被喚醒
管程機制
-
一個管程定義了一個數據結構和能爲併發進程所執行的一組操作,這組操作能同步進程和改變管程中的數據
-
組成
- ①管程的名稱
- ②局部於管程的共享數據結構說明
- ③對該數據結構進行操作的一組過程
- ④對局部於管程的共享數據結構設置初始值的語句
-
特性
- 模塊化,管程可以單獨編譯
- 抽象數據類型,管程中數據和對數據的操作
- 信息掩蔽,指管程中的數據結構只能被管程中的過程訪問
-
和進程區別
- 雖然二者都定義了數據結構,但進程定義的使私有數據結構PCB,管程定義的是公共數據結構
- 二者都存在對各自數據結構上的操作,但進程是由順序程序執行有關操作,而管程是同步操作和初始化操作
- 設置進程的目的在於實現系統的併發性,而管程的設置是爲了解決共享資源互斥使用問題
- 進程通過調用管程中的過程對共享數據結構進行操作,管程是被動工作方式,進程是主動工作方式
- 進程之間能併發執行,而管程則不能與器調用併發
- 進程具有動態性,而冠城則是操作系統中的一個資源管理模塊,共進程調用
經典進程的同步問題
生產者-消費者問題
-
利用記錄型信號量解決生產者—消費者問題
假定生產者和消費者之間有 n 個緩衝區,這時可利用互斥信號量 mutex 實現諸進程對緩衝池的互斥使用;信號量 empty 和 full 分別表示緩衝池中空緩衝區和滿緩衝區的數量。mutex 初值是 1,full 初值爲 0,empty 初值爲 N,
full + empty = N
。int in=0,out=0; item buffer[n]; // mutex=1 只有 1 個信號量供共同佔用 semaphore mutex=1,empty=n,full=0; // 信號量和初值一定要有 void producer() { // n = 1 時,mutex 就不再需要 do { produce an item in nextp; … wait(empty); wait(mutex); buffer[in] = nextp; in = (in + l)% n; signal(mutex); signal(full); }while(TRUE) ; } void consumer() { do{ wait(full); wait(mutex); nextc = buffer(out); out = (out + l) % n; signal(mutex); signal(empty); consume an item in nextc; .... }while(TRUE); } void main() { // main 函數實際上是一個進程,這裏用兩個函數明顯不妥 cobegin procedure(); consumer(); coend; }
在進程同步管理時一個共享資源可能要設二個信號量,而在進程互斥管理時只要設一個信號量;資源信號量的 wait 與 signal 操作,也成對出現,但分別出現在不同的程序中;每個程序中的 wait 操作不能顛倒,必須先執行對資源信號量的 wait 操作,然後執行互斥信號量的 wait 操作,否則可能引起進程死鎖。
哲學家進餐問題
-
利用記錄型信號量解決哲學家進餐問題
semaphore chopstick[5]={1,1,1,1,1}; do{ wait(chopstick[i]); wait(chopstick[(i+1)%5]); //eat; signal(chopstick[i]); signal(chopstick[(i+1)%5]); //think; ... }while(TRUE);
規定奇數號哲學家先拿左邊筷子,然後再拿右邊筷子,偶數號相反
讀者寫者問題
-
允許多個進程同時讀一個共享對象,但不允許一個 Writer 進程和其他 Reader 進程或 Writer 進程同時訪問共享對象。
-
利用記錄型信號量解決讀者—寫者問題互斥信號量 Wmutex,整形變量 Readcount 表示正在讀的進程數目。當 Readcount=0 時 Reader 進程才需要執行 Wait(Wmutex) 操作。若 Wait(Wmutex) 操作成功,Reader 進程便可以去讀,便做 Readcount+1 的操作。同理,僅當 Readcount-1 爲 0 時,才須執行 signal(Wmutex) 操作,以便讓 Writer 進程寫操作。因爲 Readcount 是一個可以被多個 Reader 進程訪問的臨界資源,因此也設置一個互斥信號量 rmutex
semaphore rmutex=1,wmutex=1; int readcount=0; void reader(){ do{ wait(rmutex); if(readcount==0) wait(wmutex); readcount++; signal(rmutex); … perform read operation; … wait(rmutex); readcount--; if(readcount==0) signal(wmutex); signal(rmutex); }while(TRUE); } void writer(){ do{ wait(wmutex); perform write operation; signal(wmutex); }while(TRUE); } void main(){ cobegin reader();writer(); coend }
進程通信
- 低級進程通信:進程的互斥與同步。①效率低②通信對用戶不透明
- 利用 OS 提供的高級通信工具:①使用方便②高效地傳送大量數據,利用高級通信命令(原語)
進程通訊的類型
-
4大類:共享存儲器系統、管道通信系統、消息傳遞系統、客戶機—服務器系統
-
在共享存儲器系統中,相互通信的進程共享某些數據結構或共享存儲區,進程之間能夠通過這些空間進行通信。據此,又可把它們分成以下兩種類型:
- 基於共享數據結構的通信方式,這種通信方式僅適用於傳遞相對少量的數據,通信效率低下,屬於低級通信
- 基於共享存儲區的通信方式,屬於高級通信
-
管道(pipe)通信系統
所謂“管道”,是指用於連接一個讀進程和一個寫進程以實現它們之間通信的一個共享文件,又名 pipe 文件。
-
向管道(共享文件)提供輸入的發送進程(即寫進程)以字符流形式將大量的數據送入管道;
-
而接受管道輸出的接收進程(即讀進程)則從管道中接收(讀)數據。
由於發送進程和接收進程是利用管道進行通信的,故又稱爲管道通信。
管道機制必須提供以下三方面的協調能力:① 互斥② 同步③ 確定對方是否存在
-
-
消息傳遞系統(Message passing system)
在該機制中,進程不必藉助任何共享存儲區或數據結構,而是以格式化的消息 (message)爲單位,將通信的數據封裝在消息中,並利用操作系統提供的一組通信命令(原語),在進程間進行消息傳遞,完成進程間的數據交換。
基於消息傳遞系統的通信方式屬於高級通信方式,因其實現方式的不同,可進一步分成兩類:(1) 直接通信方式(2) 間接通信方式
-
客戶機-服務器系統(Client-Server system)
-
套接字(Socket)
最流行的網絡通信程序接口之一,被設計用在同一臺主機上多個應用程序之間的通信(即進程間的通信),主要是爲了解決多對進程同時通信時端口和物理線路的多路複用問題。
一個套接字就是一個通信標識類型的數據結構,包含了通信目的的地址、通信使用的端口號、通信網絡的傳輸層協進程坐在的網絡地址、針對客戶或服務器程序提供的不同系統調用(API函數)。包括兩類:
- 基於文件型:同一臺機器的網絡環境,基於本地文件系統支持,一個套接字關聯到一個特殊文件,通信雙方通過對其讀寫實現通信(類似管道)。
- 基於網絡型:非對稱方式通信。通信雙方的進程在不同主機的網絡環境下,被分配一對套接字,一個服務器端(接受進程),一個客戶端(服務器端)。
-
遠程過程調用和遠程方法調用
遠程過程(函數)調用 RPC(Remote Procedure Call),是一個通信協議,允許一臺主機(本地)系統上的進程調用另一臺主機(遠程)系統上的進程。
主要步驟:
- 本地過程調用者以一般方式調用遠程過程在本地關聯的客戶存根,傳遞相應的參數,然後將控制權轉移給客戶存根;
- 客戶存根執行,完成包括過程名和調用參數等信息的消息建立,將控制權轉移給本地客戶進程;
- 本地客戶進程完成與服務器的消息傳遞,將消息發送到遠程服務器進程;
- 遠程服務器進程接收消息後轉入執行,並根據其中的遠程過程名找到對應的服務器存根,將消息轉給該存根;
- 該服務器存根接到消息後,由阻塞狀態轉入執行狀態,拆開消息從中取出過程調用的參數,然後以一般方式調用服務器上關聯的過程;
- 在服務器端的遠程過程運行完畢後,將結果返回給與之關聯的服務器存根;
- 該服務器存根獲得控制權運行,將結果打包爲消息,並將控制權轉移給遠程服務器進程;
- 遠程服務器進程將消息發送回客戶端;
- 本地客戶進程接收到消息後,根據其中的過程名將消息存入關聯的客戶存根,再將控制權轉移給客戶存根;
- 客戶存根從消息中取出結果,返回給本地調用者進程,並完成控制權的轉移
-
消息傳遞通信的實現方式
-
直接消息傳遞系統
-
直接通信原語
- 對稱尋址方式。發送和接受進程都顯示地提供發送命令(原語):
send(receiver,message); // 發送一個消息給接受進程 receive(sender,message); // 接收 Sender 發送的消息
- 非對稱尋址方式。接收進程可能需要與多個發送進程通信,無法指定發送進程。
send(P,message); // 發送一個消息給進程 P receive(id,message); // 接受來自任何進程的消息,id 變量可設置爲進行通信的發送進程 id 或名字。
-
消息的格式。在單機系統環境中,可採用比較短的定長消息格式;採用變長的消息格式方便了用戶
-
進程的同步方式。不論是發送進程還是接收進程,在完成消息的發送或接收後,都存在兩種可能性,即進程或者繼續發送(或接收)或者阻塞
a. 發送進程阻塞,接受進程阻塞,這種情況主要用於進程之間緊密同步,發送進程和接受進程無緩衝時
b. 發送進程不阻塞,接受進程阻塞。這回應用最廣的進程同步方式
c. 發送進程和接受進程均不阻塞
-
通信鏈路。有兩種方式建立通信鏈路。
第一種方式是:由發送進程在通信之前用顯式的“建立連接”命令(原語)請求系統爲之建立一條通信鏈路,在鏈路使用完後拆除鏈路(計算機網絡)。
第二種方式是:發送進程無須明確提出建立的鏈路的請求,只利用系統提供的發送命令(原語),系統自動建立一條鏈路(單機系統)。
鏈路又分爲兩種:單向通信鏈路(只發或只收)、雙向通信鏈路(both)
-
信箱通信:間接通信方式
-
信箱的結構:信箱頭、信箱體
-
信箱通信原語:郵箱的創建和撤銷;消息的發送和接收。
Send(mailbox, message);//將一個消息發送到指定郵箱 Receive(mailbox, message);//從指定郵箱中接收一個消息
-
私用郵箱、公用郵箱、共享郵箱
-
-
直接消息傳遞系統實例
-
消息緩衝隊列通信機制中的數據結構
- 消息緩衝區
typedef struct message_buffer { int sender; // 發送者進程標識符 int size; // 消息長度 char *text; // 消息正文 struct message_buffer *next; // 指向下一個消息緩區的指針 }
- PCB 中有關通信的數據項
typedef struct processcontrol_block{ … struct message_buffer *mq; // 消息隊列隊前指針 semaphore mutex; // 消息隊列互斥信號量 semaphore sm; // 消息隊列資源信號量 … }
-
發送原語
void send(receiver,a) {//receiver爲接收進程表示符,a爲發送區首地址 getbuf(a.size, i); //根據a.size申請緩衝區 i.sender := a.sender; i.size := a.size; copy(i.text, a.text);//將發送區a中的信息複製到消息緩衝區i中 i.next := 0; getid(PCB set, receiver.j); //獲得接受進程的標識符 wait(j.mutex); insert(j.mq, i); //將消息緩衝區插入消息隊列 signal(j.mutex); signal(j.sm); }
-
接收原語
void receive(b) { j := internal name; //j爲接受進程內部的標識符 wait(j.sm); wait(j.mutex); remove(j.mq, i); //將消息隊列中第一個消息移出 signal(j.mutex); b.sender := i.sender; b.size := i.size; copy(b.text, i.text);//將消息緩衝區i中的信息複製到接收區b releasebuf(i);//釋放消息緩衝區 }
線程(Threads)的基本概念
- 提高程序併發執行的程度,而引入
線程的引入
-
進程:使多個程序能併發執行,提高資源利用率和系統吞吐量
-
線程:減少程序在併發執行時所付出的時空開銷。
-
進程的兩個基本屬性
- 進程是一個可擁有資源的獨立單位;
- 進程同時又是一個可獨立調度和分派的基本單位。
-
程序併發執行所需付出的時空開銷
創建進程、撤銷進程、進程切換都需要花費。
-
線程——作爲調度和分配的基本單位
-
線程和進程的比較
-
調度的基本單位
傳統 OS 中,進程作爲獨立調度和分派的基本單位,因而進程是能獨立運行的基本單位;
引入線程的 OS 中,線程是調度和分派的基本單位,因而線程是能獨立運行的基本單位。
-
併發性
引入線程的 OS 中,進程之間可以併發執行,一個進程中的多個乃至全部線程也能併發執行,不同進程的線程也能併發執行。
-
擁有資源
進程可以擁有資源,作爲系統中擁有資源的一個基本單位。
線程本身不具有系統資源,僅有一點能保證獨立運行的資源。線程還允許多個線程共享該進程的資源。
-
獨立性
同一進程中的不同線程之間的獨立性要比不同進程之間的獨立性低得多。因爲爲防止進程之間彼此干擾,每個進程都有一個獨立的地址空間和其他資源,除了貢獻全局變量外,不允許其他進程訪問。
但是同一進程中的不同線程往往是爲了提高併發性以及進行相互之間的合作而創建的,共享進程的內存地址和資源。
-
系統開銷
在創建或撤銷進程時,系統都要爲之分配和回收進程回收控制塊、分配或回收其他資源。OS 爲此所付出的開銷明顯大於線程創建或撤銷所付出的開銷。進程切換亦是如此。
-
支持多處理機系統
在多處理機系統中,對於傳統的進程(單線程進程),該進程只能運行在一個處理機上。
但對於多線程進程,就可以將一個進程中的多個線程分配到多個處理機上
線程的狀態和線程控制塊
- 線程運行的三個狀態
執行狀態、就緒狀態、阻塞狀態
- 線程控制塊 TCB
線程標識符、一組寄存器、線程運行狀態、優先級、線程專有存儲區、信號屏蔽、堆棧指針。
- 多線程 OS 中的進程屬性
進程是一個可擁有資源的基本單位。多個線程可併發執行。進程已不是可執行的實體
線程的實現方式
-
內核支持線程 KST(Kernel Supported Threads)
在 OS 中的所有進程,無論是系統進程還是用戶進程,都是在操作系統內核的支持下運行的。在內核空間爲每一個內核線程設置了一個線程控制塊,內核根據該控制塊而感知某線程的存在,並對其加以控制。四個主要優點:
-
在多處理器系統中,內核能夠同時調度同一進程中的多個線程並行執行;
-
如果進程中的一個線程被阻塞了,內核可以調度該進程中的其它線程佔有處理器運行,也可以運行其它進程中的線程;
-
內核支持線程具有很小的數據結構和堆棧,線程的切換比較快,切換開銷小;
-
內核本身也可以採用多線程技術,可以提高系統的執行速度和效率
-
-
用戶級線程 ULT(User Level Threads)
用戶級線程是在用戶空間中實現的,無需內核的支持,即用戶級線程是與內核無關的。優點:
-
線程切換不需要轉換到內核空間。
-
調度算法可以是進程專用的。
-
用戶級線程的實現與OS平臺無關。
缺點:
-
系統調用的阻塞問題。在基於進程機制的 OS 中,大多數系統調用將使進程阻塞。
-
多線程應用不能利用多處理機進行多重處理的優點,內核每次分配給一個進程的僅有一個CPU,因此,進程中僅有一個線程能執行
-
-
組合方式
-
多對一:
線程管理開銷小,效率高
如果一個線程在訪問內核時候發生阻塞,則整個進程都會發生阻塞
-
一對一:
提供了比多對一模型更好的併發功能
每創建一個一個用戶線程,相應的就需要創建一個內核線程,開銷較大,需要限制整個系統的線程數
-
多對多模型
結合上述兩個模型優點,減少線程的管理開銷和提高效率
-
線程的實現
-
內核支持線程的實現:
-
在僅設置了內核支持線程的 OS 中,一種可能的線程控制方法是,系統在創建一個新進程時,便爲它分配一個任務數據區 PTDA(Per Task Data Area),其中包括若干個線程控制塊 TCB 空間。TCB 可保存線程標識符、優先級、線程運行的 CPU 狀態等信息。
-
每當進程創建一個要線程,便爲新線程分配一個 TCB,填入信息分配資源。當 PTDA 中的 TCB 已用完,又要創建新線程時,只要線程數目未超過系統允許的線程數目(數十—數百),系統就可以再分配 TCB 空間;撤銷進程時,也應回收所有資源和 TCB,有的系統爲了減少創建開銷,並不立即回收
-
-
用戶級線程的實現
-
運行時系統(Runtime System)
實質上是用於管理和控制線程的函數(過程)的集合,其中包括用於創建和撤消線程的函數、線程同步和通信的函數,以及實現線程調度的函數等。運行時系統中的所有函數都駐留在用戶空間,並作爲用戶級線程與內核之間的接口。
-
內核控制線程
又稱爲輕型進程 LWP(Light Weight Process)。每一個進程都可擁有多個 LWP,每個 LWP 都有自己的數據結構(如 TCB)。LWP 可通過系統調用來獲得內核提供的服務,這樣,當一個用戶級線程運行時,只須將它連接到一 LWP 上,此時它便具有了內核支持線程的所有屬性。這種線程實現方式就是組合方式。
線程的創建和終止
-
線程的創建
應用程序在啓動時,通常僅有一個線程在執行——初始化線程。主要功能是用於創建新線程。
-
線程的終止
終止線程通過調用相應的函數(或系統調用)對已完成線程或異常線程執行終止操作。
有些線程(主要是系統線程),它們一旦被建立起來之後,便一直運行下去而不被終止。
在大多數的 OS 中,線程被中止後並不立即釋放它所佔有的資源,只有當進程中的其它線程執行了分離函數後,被終止的線程才與資源分離,此時的資源才能被其它線程利用
-