操作系統(2)進程的描述與控制


  一、進程的描述

  1.程序並發執行時的特征

  在引入了程序間的並發執行功能后,雖然提高了系統的吞吐量和資源利用率,但由於它們共享系統資源,以及它們為完成同一項任務而相互合作,致使在這些並發執行的程序之間必將形成相互制約的關系,由此會給程序並發執行帶來新的特征。

  (1)間斷性。程序在並發執行,由於它們共享系統資源,以及為完成同一項任務而相互合作,致使在這些並發執行的程序之間形成了相互制約的關系。相互制約將導致並發程序具有“執行-暫停-執行”這種間斷性的活動規律。

  (2)失去封閉性。當系統中存在着多個可以並發執行的程序時,系統中的各種資源將為它們所共享,而這些資源的狀態也由這些程序來改變,致使其中任一程序在運行時,其環境都必然會受到其他程序的影響。程序的運行已失去了封閉性。

  (3)不可再現性。程序在並發執行時,由於失去了封閉性,也將導致其又失去可再現性。即程序經過多次執行后,雖然它們執行時的環境和初始條件相同,但得到的結果卻各不相同。

 

  2.進程的定義

  為了能使程序並發執行,並且可以對並發執行的程序加以描述和控制,引入了“進程”的概念。

  為了使參與並發執行的每個程序(含數據)都能獨立地運行,在操作系統中必須為之配置一個專門的數據結構,稱為進程控制塊(Process Control Block,PCB)。系統利用PCB來描述進程的基本情況和活動過程,進而控制和管理進程。這樣,由程序段、相關的數據段和 PCB 三部分便構成了進程實體(又稱進程映像)。一般情況下,把進程實體就簡稱為進程。

  進程的定義是:

  (1)進程是程序的一次執行。

  (2)進程是一個程序及其數據在處理機上順序執行時所發生的活動。

  (3)進程是具有獨立功能的程序在一個數據集合上運行的過程,它是系統進行資源分配和調度的一個獨立單位。

  在引入了進程實體的概念后,我們可以把傳統OS中的進程定義為:“進程是進程實體的運行過程,是系統進行資源分配和調度的一個獨立單位”。

  

  3.進程的特征

  (1)動態性。進程的實質是進程實體的執行過程,因此,動態性就是進程的最基本的特征。動態性還表現在:“它由創建而產生,由調度而執行,由撤銷而消亡。”

  (2)並發性。多個進程實體同時存在於內存,且能在一段時間內同時運行。並發性是進程的另一個重要特征。

  (3)獨立性。進程實體是一個能獨立運行、獨立獲得資源和獨立接收調度的基本單位。

  (4)異步性。進程是按異步方式運行的,即按各自獨立的、不可預知的速度向前推進。

 

  4.進程的三種基本狀態

(1)就緒(Ready)狀態。指進程已處於准備好運行的狀態,即進程已分配到除 CPU 以外的所有必要資源后,主要再獲得 CPU 資源,便可立即執行。如果系統中有許多處於就緒狀態的進程,通常將它們按一定的策略(如優先級策略)排成一個隊列,稱該隊列為就緒隊列。

(2)執行(Running)狀態。指進程已獲得 CPU 資源,其程序正在執行的狀態。對任何一個時刻而言,在單處理機系統中,只有一個進程處於狀態,而在多處理機系統中,則有多個進程處於執行狀態。

(3)阻塞(Block)狀態。指正在執行的進程由於發生某事件(如 I/O 請求、申請緩沖區失敗等)暫時無法繼續執行時的狀態,即進程的執行收到阻塞。此時引起進程調度, OS 會把處理機分配給另一個就緒進程,而讓受阻進程處於暫停狀態,一般將這種狀態稱為阻塞狀態,也稱為等待狀態或封鎖狀態。通常系統將處於阻塞狀態的進程也排成一個隊列,稱該隊列為阻塞隊列。

 

  5.三種基本狀態的轉換

  (1)處於就緒狀態的進程,在調度程序為之分配了處理機之后便可執行,相應地,其狀態就由就緒狀態變為執行狀態;

  (2)正在執行的進程(當前進程)如果因分配給它的時間片已完而被剝奪處理機暫停執行時,其狀態便由執行轉為就緒

  (3)如果因發生某事件,致使當前進程的執行受阻(例如進程訪問某臨界資源,而該資源整備其他進程訪問時),使之無法繼續執行,則該進程狀態將由執行轉為阻塞。

 

  6.創建狀態和終止狀態

  為了滿足進程控制塊對數據以及操作的完整性要求以及增強管理的靈活性,通常在系統中又為進程引入了兩種常見的狀態:創建狀態和終止狀態。

  (1)創建狀態

    創建一個進程的過程:

  • 首先由進程申請一個空白PCB,並向 PCB 中填寫用於控制和管理進程的信息;
  • 然后為該進程分配運行時所必須的資源
  • 把該進程轉入就緒狀態並插入就緒隊列之中。

    如果進程所需的資源尚不能得到滿足,必須系統尚無足夠的內存使進程無法裝入其中,此時創建工作尚未完成,進程不能被調度運行,此時就稱為創建狀態。

  引入創建狀態,是為了保證進程的調度必須在創建工作完成后進行,以確保對進程控制塊操作的完整性。同時,創建狀態的引入,也增加了管理的靈活性,操作系統可以根據系統性能或主存容量的限制,推遲創建狀態進程的提交。對於處於創建狀態的進程,獲得了其所必需的資源,以及對其PCB初始化工作完成后,進程狀態便可由創建狀態轉入就緒狀態。

  (2)終止狀態

  終止一個進程的過程:

  • 等待操作系統進行善后處理
  • 將其 PCB 清零,並將 PCB 空間返還系統。

  當一個進程到達了自然結束點,或是出現了無法克服的錯誤,或是被操作系統所終結,或是被其他有終止權的進程所終結,它將進入終止狀態。進入終止態的進程以后不能再執行,但在操作系統中依然保留一個記錄,其中保存狀態碼和一些計時統計數據,供其它進程收集。一旦其它進程完成了對終止狀態進程的信息提取之后,操作系統將刪除該進程。

 

  7.掛起操作和進程狀態的轉換

  為了系統和用戶觀察和分析進程的需要,還引入了一個對進程的重要操作--掛起操作。當該操作作用於某個進程時,該進程將被掛起,意味着此時該進程處於靜止狀態。如果進程正在執行,它將暫停執行。若原本處於就緒狀態,則該進程此時暫不接受調度。與掛起操作對應的操作是激活操作。

  (1)掛起操作的引入原因

  • 終端用戶的請求。當終端用戶在自己的程序運行期間發現有可疑問題時,希望暫時使自己的程序靜止下來。亦即,使正在執行的進程暫停執行;若此時用戶進程正處於就緒狀態而未執行,則該進程暫不接受調度,以便用戶研究其執行情況或對程序進行修改。我們把這種靜止狀態稱為掛起狀態。
  • 父進程請求。有時父進程希望掛起自己的某個子進程,以便考查和修改該子進程,或者協調各子進程間的活動。
  • 負荷調節的需要。當實時系統中的工作負荷較重,已可能影響到對實時任務的控制時,可由系統把一些不重要的進程掛起,以保證系統能正常運行。
  • 操作系統的需要。操作系統有時希望掛起某些進程,以便檢查運行中的資源使用情況或進行記賬。

  (2)引入掛起原語 Suspend 和激活原語 Active 后三個進程狀態的轉換

  • 活動就緒→靜止就緒。當進程處於未被掛起的就緒狀態時,稱此為活動就緒狀態,表示為 Readya。當用掛起原語 Suspend 將該進程掛起后,該進程便轉變為靜止就緒狀態,表示為 Readys,處於 Readys 狀態的進程不再被調度執行。
  • 活動阻塞→靜止阻塞。當進程處於未被掛起的阻塞狀態時,稱它是處於活動阻塞狀態,表示為 Blockeda。當用 Suspend 原語將它掛起后,進程便轉變為靜止阻塞狀態,表示為Blockeds。處於該狀態的進程在其所期待的事件出現后,將從靜止阻塞變為靜止就緒。
  • 靜止就緒→活動就緒。處於 Readys 狀態的進程,若用激活原語 Active 激活后,該進程將轉變為 Readya 狀態。
  • 靜止阻塞→活動阻塞。處於 Blockeds 狀態的進程,若用激活原語 Active 激活后,該進程將轉變為 Blockeda 狀態。

    

  (3)引入掛起原語 Suspend 和激活原語 Active 后五個進程狀態的轉換

  • NULL→創建:一個新進程產生時,該進程處於創建狀態。
  • 創建→活動就緒:在當前系統的性能和內存的容量均允許的情況下,完成對進程創建的必要操作后,相應的系統進程將進程的狀態轉換為活動就緒狀態。
  • 創建→靜止就緒:考慮到系統當前資源狀況和性能要求,並不分配給新建進程所需資源,主要是主存資源,相應的系統進程將進程狀態轉為靜止就緒狀態,對換到外存,不再參與調度,此時進程創建工作尚未完成。
  • 執行→終止:當一個進程到達了自然結束點,或是出現了無法克服的錯誤,或是被操作系統所終結,或是被其他有終止權的進程所終結,進程即進終止狀態。
  •  

  8.進程管理中的數據結構

  (1)操作系統中用於管理控制的數據接口

    在操作系統中,對於每個系統和每個進程都設置了一個數據結構,用於表征其實體,稱之為資源信息表或進程信息表。

    OS 管理的這些數據結構一般分為四類:內存表、設備表、文件表和用於進程管理的進程表。其中,進程表又被稱為進程控制塊 PCB。

  (2)進程控制塊 PCB 的作用。

  進程控制塊的作用是使一個在多道程序環境下不能獨立運行的程序(含數據),成為一個能獨立運行的基本單位,一個能與其它進程並發執行的進程。或者說,OS 是根據 PCB 來對並發執行的進程進行控制和管理的。

  • 作為獨立運行基本單位的標志。
  • 能實現間斷性運行方式。
  • 提供進程管理所需要的信息。
  • 提供進程調度所需要的信息。
  • 實現與其他進程的同步與通信。

  (3)進程控制塊 PCB 中的信息

  • 進程標識符。進程標識符用於惟一地標識一個進程。一個進程通常有兩種標識符:①內部標識符。在所有的操作系統中,都為每一個進程賦予了一個惟一的數字標識符,它通常是一個進程的序號。設置內部標識符主要是為了方便系統使用。②外部標識符。它由創建者提供,通常是由字母、數字組成,往往是由用戶(進程)在訪問該進程時使用。為了描述進程的家族關系,還應設置父進程標識及子進程標識。此外,還可設置用戶標識,以指示擁有該進程的用戶。
  • 處理機狀態。處理機狀態信息主要是由處理機的各種寄存器中的內容組成的。處理機在運行時,許多信息都放在寄存器中。當處理機被中斷時,所有這些信息都必須保存在 PCB 中,以便在該進程重新執行時,能從斷點繼續執行。這些寄存器包括:① 通用寄存器,又稱為用戶可視寄存器,它們是用戶程序可以訪問的,用於暫存信息,在大多數處理機中,有 8~32 個通用寄存器,在 RISC 結構的計算機中可超過 100 個;② 指令計數器,其中存放了要訪問的下一條指令的地址;③ 程序狀態字 PSW,其中含有狀態信息,如條件碼、執行方式、中斷屏蔽標志等;④ 用戶棧指針,指每個用戶進程都有一個或若干個與之相關的系統棧,用於存放過程和系統調用參數及調用地址,棧指針指向該棧的棧頂。
  • 進程調度信息。在 PCB 中還存放一些與進程調度和進程對換有關的信息,包括:① 進程狀態,指明進程的當前狀態,作為進程調度和對換時的依據;② 進程優先級,用於描述進程使用處理機的優先級別的一個整數,優先級高的進程應優先獲得處理機;③ 進程調度所需的其它信息,它們與所采用的進程調度算法有關,比如,進程已等待 CPU 的時間總和、進程已執行的時間總和等;④ 事件,指進程由執行狀態轉變為阻塞狀態所等待發生的事件,即阻塞原因。
  • 進程控制信息。進程控制信息包括:① 程序和數據的地址,指進程的程序和數據所在的內存或外存地(首)址,以便再調度到該進程執行時,能從 PCB 中找到其程序和數據;② 進程同步和通信機制,指實現進程同步和進程通信時必需的機制,如消息隊列指針、信號量等,它們可能全部或部分地放在 PCB 中;③ 資源清單,即一張列出了除 CPU 以外的、進程所需的全部資源及已經分配到該進程的資源的清單;④ 鏈接指針,它給出了本進程(PCB)所在隊列中的下一個進程的 PCB 的首地址。

  (4)進程控制塊的組織方式

    在一個系統中,通常可擁有數十個、數百個乃至數千個 PCB。為了能對它們加以有效的管理,應該用適當的方式將這些 PCB 組織起來。常用的組織方式有三種:

  • 線性方式。即將系統中所有的 PCB 都組織在一張線性表中,將該表的首地址存放在內存的一個專用區域中。該方式實現簡單、開銷小,但每次查找時都需要掃描整張表,因此適合進程數據不多的系統。
  • 鏈接方式。即把具有相同狀態進程的 PCB 分別通過 PCB 中的鏈接字鏈接成一個隊列。這樣,可以形成就緒隊列、若干個阻塞隊列和空白隊列等。對就緒隊列來說,往往按進程的優先級將 PCB 從高到低進行排列,將優先級高的進程 PCB 排在隊列的前面。同樣,也可以把處於阻塞狀態進程的 PCB 根據其阻塞原因的不同,排成多個隊列,如等待 I/O 操作完成的隊列和等待分配內存的隊列等。
  • 索引方式。

 

  二、進程的控制

  進程控制是進程管理中最基本的功能,主要包括創建新進程、終止已完成的進程、將因發生異常情況而無法繼續運行的進程置於阻塞狀態、負責進程運行中的狀態轉換等功能。

  如當一個正在執行的進程因等待某事件而暫時不能繼續執行時,將其轉換為阻塞狀態,而當該進程所期待的事件出現時,又將該進程轉換為就緒狀態等等。進程控制一般是由 OS 的內核中的原語來實現的。

  1.操作系統內核  

  現代操作系統一般將 OS 划分為若干層次,再將 OS 的不同功能分別設置在不同的層次中。通常將一些與硬件緊密相關的模塊(如中斷處理程序等)、各種常用設備的驅動程序以及運行頻率較高的模塊(如時鍾管理、進程調度和許多模塊所公用的一些基本操作),都安排在緊靠硬件的軟件層次中,將它們常駐內存,即通常被稱為 OS 內核。這種安排方式的目的在於:①便於對這些軟件進行保護,防止遭受其他應用程序的破壞;②可以提高 OS 的運行效率。

  相對應的是,為了防止 OS 本身及關鍵數據(如 PCB 等)遭受到應用程序有意或無意的破壞,通常也將處理機的執行狀態分為系統態和用戶態兩種:

  • 系統態。又稱為管態,也稱為內核態。它具有較高的特權,能執行一切指令,訪問所有寄存器和存儲區,傳統的 OS 都在系統態運行。
  • 用戶態。又稱為目態。它是具有較低特權的執行狀態,僅能執行規定的指令,訪問指定的寄存器和存儲區。一般情況下,應用程序只能在用戶態運行,不能去執行 OS 指令以及 訪問 OS 區域。這樣可以防止應用程序對 OS 的破壞。

  大多數 OS 內核都包含了兩個方面的功能:

  (1)支撐功能

該功能是提供給 OS 其他眾多模塊所需要的一些基本功能,以便支撐這些模塊工作、其中三種最基本的支撐功能是:中斷處理、時鍾管理和原語操作。

  • 中斷處理。中斷處理時內核最基本的功能,是整個操作系統賴以活動的基礎、OS 中許多重要的活動,如各種類型的系統調用、鍵盤命令的輸入、進程調度、設備驅動等,無不依賴於中斷。通常,為減少處理機中斷的時間,提高程序執行的並發性,內核在對中斷進行“有限處理”后,便轉入相關的進程,由這些進程繼續完成后續的處理工作。
  • 時鍾管理。時鍾管理是內核的一項基本功能,在 OS 中的許多活動都需要得到它的支撐,如在時間片輪轉調度中,每當時間片 用完時,便由時鍾管理產生一個中斷信號,促使調度程序重新進行調度。同樣,在實時系統中的截止時間控制、批處理系統中的最長運行時間控制等,也無不依賴於時鍾管理功能。
  • 原語操作。原語(Primitive)是由若干條指令組成的,用於完成一定功能的一個過程。它與一般過程的區別在於:它們是“原子操作(Action Operation)”。所謂原子操作,是指一個操作中的所
    有動作要么全做,要么全不做。換言之,它是一個不可分割的基本單位,因此,在執行過程中不允許被中斷。原子操作在管態下執行,常駐內存。原語的作用是為了實現進程的通信和控制,系統對進程的控制如不使用原語,就會造成其狀態的不確定性,從而達不到進程控制的目的。

  (2)資源管理功能

  • 進程管理。在進程管理中,或者由於各個功能模塊的運行頻率較高,如進程的調度與分派、進程的創建與撤銷等;或者由於它們為多種功能模塊所需要,如用於實現進程同步的原語、常用的進程通信原語等。通常都將它們放在內核中,以提高 OS 的性能。
  • 存儲器管理。存儲器管理軟件的運行頻率也比較高,如用於實現將用戶控件的邏輯地址變換為內存空間的物理地址的地址轉換機構、內存分配與回收的功能模塊以及實現內存保護和對換功能的模塊等。通常也將它們放在內核中,以保證存儲器管理具有較高的運行速度。
  • 設備管理。由於設備管理與硬件(設備)緊密相關,因此其中很大部分也都設置在內核中。如各類設備的驅動程序、用於緩存 CPU 與 I/O 速度不匹配矛盾的緩存管理、用於實現設備分配和設備獨立性功能的模塊等。

  2.進程的創建

  (1)引起進程創建的事件

在多道程序環境中,只有(作為)進程(時)才能在系統中運行。因此,為使程序能運行,就必須為它創建進程。導致一個進程去創建另一個進程的典型事件,可有以下四類:

  • 用戶登錄。在分時系統中,用戶在終端鍵入登錄命令后,如果是合法用戶,系統將為該終端建立一個進程,並把它插入就緒隊列中。
  • 作業調度。在批處理系統中,當作業調度程序按一定的算法調度到某作業時,便將該作業裝入內存,為它分配必要的資源,並立即為它創建進程,再插入就緒隊列中。
  • 提供服務。當運行中的用戶程序提出某種請求后,系統將專門創建一個進程來提供用戶所需要的服務,例如,用戶程序要求進行文件打印,操作系統將為它創建一個打印進程,這樣,不僅可使打印進程與該用戶進程並發執行,而且還便於計算出為完成打印任務所花費的時間。
  • 應用請求。在上述三種情況下,都是由系統內核為它創建一個新進程;而第 4 類事件則是基於應用進程的需求,由它自己創建一個新進程,以便使新進程以並發運行方式完成特定任務。例如,某應用程序需要不斷地從鍵盤終端輸入數據,繼而又要對輸入數據進行相應的處理,然后,再將處理結果以表格形式在屏幕上顯示。該應用進程為使這幾個操作能並發執行,以加速任務的完成,可以分別建立鍵盤輸入進程、表格輸出進程。

  (2)進程的創建過程

一旦操作系統發現了要求創建新進程的事件后,便調用進程創建原語 Creat 按下述步驟創建一個新進程。

  • 申請空白 PCB。為新進程申請獲得惟一的數字標識符,並從 PCB 集合中索取一個空白 PCB。
  • 為新進程分配資源。為新進程的程序和數據以及用戶棧分配必要的內存空間。顯然,此時操作系統必須知道新進程所需內存的大小。對於批處理作業,其大小可在用戶提出創建進程要求時提供。若是為應用進程創建子進程,也應是在該進程提出創建進程的請求中給出所需內存的大小。對於交互型作業,用戶可以不給出內存要求而由系統分配一定的空間。如果新進程要共享某個已在內存的地址空間(即已裝入內存的共享段),則必須建立相應的鏈接。
  • 初始化進程控制塊。PCB 的初始化包括:① 初始化標識信息,將系統分配的標識符和父進程標識符填入新 PCB 中;② 初始化處理機狀態信息,使程序計數器指向程序的入口地址,使棧指針指向棧頂;③ 初始化處理機控制信息,將進程的狀態設置為就緒狀態或靜止就緒狀態,對於優先級,通常是將它設置為最低優先級,除非用戶以顯式方式提出高優先級要求。
  • 將新進程插入就緒隊列,如果進程就緒隊列能夠接納新進程,便將新進程插入就緒隊列。

  3.進程的終止

  (1)引起進程終止的事件

  • 正常結束。表示進程的任務已經完成,准備退出運行。在任何計算機系統中,都應有一個用於表示進程已經運行完成的指示。在批處理系統中,通常在程序的最后安排一條 Holt 指令或終止的系統調用。當程序運行到 Holt 指令時,將產生一個中斷,去通知 OS 本進程已經完成。在分時系統中,用戶可利用 Logs off 去表示進程運行完畢,此時同樣可產生一個中斷,去通知 OS 進程已運行完畢。
  • 異常結束。是指進程在運行時發生了某種異常事件,使程序無法繼續運行。這類異常事件很多,常見的有下述幾種:
  1. 越界錯誤。這是指程序所訪問的存儲區已越出該進程的區域。
  2. 保護錯。這是指進程試圖去訪問一個不允許訪問的資源或文件,或者以不適當的方式進行訪問,例如,進程試圖去寫一個只讀文件。
  3. 非法指令。這是指程序試圖去執行一條不存在的指令。出現該錯誤的原因,可能是程序錯誤地轉移到數據區,把數據當成了指令。
  4. 特權指令錯。這是指用戶進程試圖去執行一條只允許 OS 執行的指令。
  5. 運行超時。這是指進程的執行時間超過了指定的最大值。
  6. 等待超時。這是指進程等待某事件的時間超過了規定的最大值。
  7. 算術運算錯。這是指進程試圖去執行一個被禁止的運算,例如被 0 除。
  8. I/O 故障。這是指在 I/O 過程中發生了錯誤等。
  • 外界干預。外界干預並非指在本進程運行中出現了異常事件,而是指進程因外界的請求而終止運行。這些干預有:
  1. 操作員或操作系統干預。由於某種原因,例如,發生了死鎖,由操作員或操作系統終止該進程。
  2. 父進程請求。由於父進程具有終止自己的任何子孫進程的權力,因而當父進程提出請求時,系統將終止該進程。
  3. 父進程終止。當父進程終止時,OS 也將它的所有子孫進程終止。

  (2)進程的終止過程

如果系統中發生了上述要求終止進程的某事件,OS 便調用進程終止原語,按下述過程去終止指定的進程。

  • 根據被終止進程的標識符,從 PCB 集合中檢索出該進程的 PCB,從中讀出該進程的狀態。
  • 若被終止進程正處於執行狀態,應立即終止該進程的執行,並置調度標志為真,用於指示該進程被終止后應重新進行調度。
  • 若該進程還有子孫進程,還應將其所有子孫進程予以終止,以防它們成為不可控的進程。
  • 將被終止進程所擁有的全部資源,或者歸還給其父進程,或者歸還給系統。
  • 將被終止進程(PCB)從所在隊列(或鏈表)中移出,等待其他程序來搜集信息。

  4.進程的阻塞與喚醒

  (1)引起進程阻塞和喚醒的事件

  • 向系統請求共享資源失敗。進程在向系統請求共享資源時,由於系統已無足夠的資源分配給它,此時進程因不能繼續運行而轉變為阻塞狀態。例如,一進程請求使用某資源,如打印機,由於系統已將打印機分配給其他進程而不能分配給請求進程,這時請求者進程只能被阻塞,僅在其他進程在釋放出打印機的同時,才將請求進程喚醒。
  • 等待某種操作的完成。當進程啟動某種操作后,如果該進程必須在該操作完成之后才能繼續執行,則必須先使該進程阻塞,以等待該操作完成。例如,進程啟動了某 I/O 設備,如果只有在 I/O 設備完成了指定的 I/O 操作任務后進程才能繼續執行,則該進程在啟動了 I/O 操作后,便自動進入阻塞狀態去等待。在 I/O 操作完成后,再由中斷處理程序或中斷進程將該進程喚醒。
  • 新數據尚未到達。對於相互合作的進程,如果其中一個進程需要先獲得另一(合作)進程提供的數據后才能對數據進行處理,則只要其所需數據尚未到達,該進程只有(等待)阻塞。例如,有兩個進程,進程 A 用於輸入數據,進程 B 對輸入數據進行加工。假如 A 尚未將數據輸入完畢,則進程B 將因沒有所需的處理數據而阻塞;一旦進程 A 把數據輸入完畢,便可去喚醒進程 B。
  • 等待新任務的到達。在某些系統中,特別是網絡環境下的 OS,往往設置一些具有某特定功能的系統進程,每當這種進程完成任務后,便把自己阻塞起來以等待新任務到來。例如,在網絡環境中的發送進程,其主要任務是發送數據包,若已有的數據包已全部發送完成而又無新的發送請求,這時(發送)進程將使自己進入阻塞狀態; 僅當又有進程提出新的發送請求時,才將發送進程喚醒。

  (2)進程阻塞過程

  正在執行的進程,當發現上述某事件時,由於無法繼續執行,於是進程便通過調用阻塞原語 block 把自己阻塞。可見,進程的阻塞是進程自身的一種主動行為。進入 block 過程
后,由於此時該進程還處於執行狀態,所以應先立即停止執行,把進程控制塊中的現行狀態由“執行”改為“阻塞”,並將 PCB 插入阻塞隊列。如果系統中設置了因不同事件而阻塞的多個阻塞隊列,則應將本進程插入到具有相同事件的阻塞(等待)隊列。最后,轉調度程序進行重新調度,將處理機分配給另一就緒進程並進行切換,亦即,保留被阻塞進程的處理機狀態(在 PCB 中),再按新進程的 PCB 中的處理機狀態設置 CPU 的環境。

  (3)進程喚醒過程

  當被阻塞進程所期待的事件出現時,如 I/O 完成或其所期待的數據已經到達,則由有關進程(比如用完並釋放了該 I/O 設備的進程)調用喚醒原語 wakeup,將等待該事件的進程喚醒。喚醒原語執行的過程是:首先把被阻塞的進程從等待該事件的阻塞隊列中移出,將其 PCB 中的現行狀態由阻塞改為就緒,然后再將該 PCB 插入到就緒隊列中。應當指出,block 原語和 wakeup 原語是一對作用剛好相反的原語。因此,如果在某進程中調用了阻塞原語,則必須在與之相合作的另一進程中或其他相關的進程中安排喚醒原語,以能喚醒阻塞進程;否則,被阻塞進程將會因不能被喚醒而長久地處於阻塞狀態,從而再無機會繼續運行。

  5.進程的掛起與激活

  (1)進程的掛起

  當出現了引起進程掛起的事件時,比如,用戶進程請求將自己掛起,或父進程請求將自己的某個子進程掛起,系統將利用掛起原語 suspend( )將指定進程或處於阻塞狀態的進程掛起。掛起原語的執行過程是:首先檢查被掛起進程的狀態,若處於活動就緒狀態,便將其改為靜止就緒; 對於活動阻塞狀態的進程,則將之改為靜止阻塞。為了方便用戶或父進程考查該進程的運行情況而把該進程的 PCB 復制到某指定的內存區域。最后,若被掛起的進程正在執行,則轉向調度程序重新調度。

  (2)進程的激活

  當發生激活進程的事件時,例如,父進程或用戶進程請求激活指定進程,若該進程駐留在外存而內存中已有足夠的空間時,則可將在外存上處於靜止就緒狀態的該進程換入內存。這時,系統將利用激活原語 active( )將指定進程激活。激活原語先將進程從外存調入內存,檢查該進程的現行狀態,若是靜止就緒,便將之改為活動就緒;若為靜止阻塞,便將之改為活動阻塞。假如采用的是搶占調度策略,則每當有新進程進入就緒隊列時,應檢查是否要進行重新調度,即由調度程序將被激活進程與當前進程進行優先級的比較,如果被激活進程的優先級更低,就不必重新調度;否則,立即剝奪當前進程的運行,把處理機分配給剛被激活的進程。

 

  三、進程的同步

  在 OS 中引入進程后,一方面可以使系統中的多道程序並發執行,這不僅能有效地改善資源利用率,還可以顯著地提高系統的吞吐量,但另一方面卻使系統變得更加復雜。如果不能采取有效的措施,對多個進程的運行進行妥善的管理,必然會因為這些進程對系統資源的無序爭奪給系統造成混亂。致使每次處理的結果存在着不確定性,即顯現出不可再現性。

  為保證多個進程能有條不紊地運行,在多道程序系統中,必須引入進制同步機制。進程同步進制分為硬件同步機制、信號量機制和管程機制等。

  (1)進程同步的基本概念

    在多道程序環境下,對於同處於一個系統中的多個進程,由於它們共享系統中的資源,或為完成某一任務而相互合作,它們之間可能存在兩種形式的制約關系。

  • 間接相互制約的關系。同處於一個系統中的進程,通常都共享着某種系統資源,如共享 CPU、共享 I/O 設備等。所謂間接相互制約即源於這種資源共享,例如,有兩個進程 A和 B,如果在 A 進程提出打印請求時,系統已將惟一的一台打印機分配給了進程 B,則此時進程 A 只能阻塞;一旦進程 B 將打印機釋放,則 A 進程才能由阻塞改為就緒狀態。
  • 直接相互制約的關系。這種制約主要源於進程間的合作。例如,有一輸入進程 A 通過單緩沖向進程 B 提供數據。當該緩沖空時,計算進程因不能獲得所需數據而阻塞,而當進程 A 把數據輸入緩沖區后,便將進程 B 喚醒;反之,當緩沖區已滿時,進程 A 因不能再向緩沖區投放數據而阻塞,當進程 B 將緩沖區數據取走后便可喚醒 A。

    許多硬件資源如打印機、磁帶機等,都屬於臨界資源(Critical Resouce),諸進程間應采取互斥方式,實現對這種資源的共享。

  不論是硬件臨界資源,還是軟件臨界資源,多個進程必須互斥地對它進行訪問。人們把在每個進程中訪問臨界資源的那段代碼稱為臨界區(critical section)。顯然,若能保證諸進程互斥地進入自己的臨界區,便可實現諸進程對臨界資源的互斥訪問。為此,每個進程在進入臨界區之前,應先對欲訪問的臨界資源進行檢查,看它是否正被訪問。如果此刻該臨界資源未被訪問,進程便可進入臨界區對該資源進行訪問,並設置它正被訪問的標志;如果此刻該臨界資源正被某進程訪問,則本進程不能進入臨界區。因此,必須在臨界區前面增加一段用於進行上述檢查的代碼,把這段代碼稱為進入區(entry section)。相應地,在臨界區后面也要加上一段稱為退出區(exit section)的代碼,用於將臨界區正被訪問的標志恢復為未被訪問的標志。進程中除上述進入區、臨界區及退出區之外的其它部分的代碼,在這里都稱為剩余區。即where(true) {進入區 臨界區 退出區 剩余區}

    為實現進程互斥地進入自已的臨界區,可用軟件方法,更多的是在系統中設置專門的同步機構來協調各進程間的運行。所有同步機制都應遵循下述四條准則:

  • 空閑讓進。當無進程處於臨界區時,表明臨界資源處於空閑狀態,應允許一個請求進入臨界區的進程立即進入自己的臨界區,以有效地利用臨界資源。
  • 忙則等待。當已有進程進入臨界區時,表明臨界資源正在被訪問,因而其它試圖進入臨界區的進程必須等待,以保證對臨界資源的互斥訪問。
  • 有限等待。對要求訪問臨界資源的進程,應保證在有限時間內能進入自己的臨界區,以免陷入“死等”狀態。
  • 讓權等待。當進程不能進入自己的臨界區時,應立即釋放處理機,以免進程陷入“忙等”狀態。

  (2)硬件同步機制

  • 關中斷。實現互斥最簡單的方法之一。在進入鎖測試之前關閉中斷,直到完成鎖測試並上鎖之后才能打開終端。這樣,進程在臨界區執行期間,計算機系統不響應終端,從而不會引發調度,也就不會發生進程或線程切換。由此,保證了對鎖的測試和關鎖操作的連續性和完整性,有效地保證了互斥。
  • 利用 Test-and-Set 指令實現互斥。借助硬件指令-- TS 指令實現互斥。
  • 利用 Swap 指令實現進程互斥。該指令稱為對換指令,在 Inter 80x86 中又稱為 XCHG 指令,用於交換兩個字的內容。

  (3)信號量機制

    信號量從整型型號量經記錄信號量,進而發展為“信號量集”機制。現在,信號量機制已被廣泛地應用於單處理機和多處理機系統以及計算機網絡中。

    1.整型信號量

  整型信號量定義為一個用於表示資源數目的整型量 S,它與一般整形量不同,除初始化外,僅能通過兩個標准的原子操作 wait(S) 和 signal(S)來對 S 進行加或減操作。這兩個操作被稱為 P、V操作。由於 wait(S) 和 signal(S) 是兩個原子操作,因此,它們在執行時是不可中斷的。即當一個進程在修改某信號量時,沒有其他進程可同時對該信號量進行修改。

wait(S) {
    wait(S <= 0);
    S--;
}

signal(S) {
    S++;
}

2.記錄性信號量

  在整型信號量機制中的 wait 操作,只要是信號量 S <= 0,就會不斷地測試。因此,該機制並未遵循“讓權等待”的准則,而是使進程處於“忙等”的狀態。記錄性信號量則是一種不存在“忙等”現象的進程同步機制。但在采取了“讓權等待”的策略后,又會出現多個進程等待訪問同一臨界資源的情況。為此,在信號量機制中,除了需要一個用於代表資源數目的整型變量 value 外,還應增加一個進程鏈表指針 L,用於鏈接上述的所有等待進程。記錄型信號量是由於它采用了記錄型的數據結構而得名的。

    3.AND 型信號量

  AND 同步機制的基本思想是:將進程在整個運行過程中需要的所有資源,一次性全部地分配給進程,待進程使用完后再一起釋放。只要尚有一個資源未能分配給進程,其它所有可能為之分配的資源也不分配給它。亦即,對若干個臨界資源的分配,采取原子操作方式:要么把它所請求的資源全部分配到進程,要么一個也不分配。由死鎖理論可知,這樣就可避免上述死鎖情況的發生。為此,在 wait 操作中,增加了一個“AND”條件,故稱為AND 同步,或稱為同時 wait 操作,

4.信號量集

  在記錄型信號量機制中,wait(S)或 signal(S)操作僅能對信號量施以加 1 或減 1 操作,意味着每次只能獲得或釋放一個單位的臨界資源。而當一次需要 N 個某類臨界資源時,便要進行 N 次 wait(S)操作,顯然這是低效的。此外,在有些情況下,當資源數量低於某一下限值時,便不予以分配。因而,在每次分配之前,都必須測試該資源的數量,看其是否大於其下限值。基於上述兩點,可以對 AND 信號量機制加以擴充,形成一般化的“信號量集”機制。

  (4)管程機制

  雖然信號量機制是一種既方便、又有效的進程同步機制,但每個要訪問臨界資源的進程都必須自備同步操作 wait(S)和 signal(S)。這就使大量的同步操作分散在各個進程中。這不僅給系統的管理帶來了麻煩,而且還會因同步操作的使用不當而導致系統死鎖。這樣,在解決上述問題的過程中,便產生了一種新的進程同步工具——管程(Monitors)。

  系統中的各種硬件資源和軟件資源,均可用數據結構抽象地描述其資源特性,即用少量信息和對該資源所執行的操作來表征該資源,而忽略了它們的內部結構和實現細節。例如,對一台電傳機,可用與分配該資源有關的狀態信息(busy 或 free)和對它執行請求與釋放的操作,以及等待該資源的進程隊列來描述。又如,一個 FIFO 隊列,可用其隊長、隊首和隊尾以及在該隊列上執行的一組操作來描述。
  利用共享數據結構抽象地表示系統中的共享資源,而把對該共享數據結構實施的操作定義為一組過程,如資源的請求和釋放過程 request 和 release。進程對共享資源的申請、釋放和其它操作,都是通過這組過程對共享數據結構的操作來實現的,這組過程還可以根據資源的情況,或接受或阻塞進程的訪問,確保每次僅有一個進程使用共享資源,這樣就可以統一管理對共享資源的所有訪問,實現進程互斥。

  代表共享資源的數據結構,以及由對該共享數據結構實施操作的一組過程所組成的資源管理程序,共同構成了一個操作系統的資源管理模塊,我們稱之為管程。管程被請求和釋放資源的進程所調用。Hansan 為管程所下的定義是:“一個管程定義了一個數據結構和能為並發進程所執行(在該數據結構上)的一組操作,這組操作能同步進程和改變管程中的數據”。
  由上述的定義可知,管程由四部分組成:① 管程的名稱;② 局部於管程內部的共享數據結構說明;③ 對該數據結構進行操作的一組過程;④ 對局部於管程內部的共享數據設置初始值的語句。

 

  四、進程通信

  進程通信,是指進程之間的信息交換。由於進程的互斥和同步,需要在進程間交換一定的信息,故不少學者也將他們歸為進程通信,但只能它它們成為低級進程通信。在進程互斥中,進程通過只修改信號量來向其他進程表明臨界資源是否可用。信號量機制作為同步工具是卓有成效的,但作為通信工具,則不夠理想,主要表現在下述兩方面:(1) 效率低,生產者每次只能向緩沖池投放一個產品(消息),消費者每次只能從緩沖區中取得一個消息;(2) 通信對用戶不透明,OS 置為進程之間的通信提供了共享存儲器。而關於進程之間通信所需之共享數據結構的設置、數據的傳送、進程的互斥與同步,都必須由程序員去實現,顯然,對於用戶而言,是非常不方便的。

  在進程之間要傳送大量數據時,應道利用 OS 提供的高級通信工具,該工具的主要特點是:(1)使用方便。OS 隱藏了實現進程通信的具體細節,向用戶提供了一組用於實現高級通信的命令(原語),用戶可方便地直接利用它實現進程之間的通信。或者說,通信過程對用戶是透明的。(2)高效地傳送大量數據。用戶可直接利用高級通信命令(原語)高效地傳送大量的數據。

  目前,高級通信機制可歸結為四大類:共享存儲器系統、管道通信系統、消息傳遞系統以及客戶機-服務器系統。

  1.共享存儲器系統

在共享存儲器系統(Shared-Memory System)中,相互通信的進程共享某些數據結構或共享存儲區,進程之間能夠通過這些空間進行通信。據此,又可把它們分成以下兩種類型:

  • 基於共享數據結構的通信方式。在這種通信方式中,要求諸進程公用某些數據結構,借以實現諸進程間的信息交換。如在生產者—消費者問題中,就是用有界緩沖區這種數據結構來實現通信的。這里,公用數據結構的設置及對進程間同步的處理,都是程序員的職責。這無疑增加了程序員的負擔,而操作系統卻只須提供共享存儲器。因此,這種通信方式是低效的,只適於傳遞相對少量的數據。
  • 基於共享存儲區的通信方式。為了傳輸大量數據,在存儲器中划出了一塊共享存儲區,諸進程可通過對共享存儲區中數據的讀或寫來實現通信。這種通信方式屬於高級通信。進程在通信前,先向系統申請獲得共享存儲區中的一個分區,並指定該分區的關鍵字;若系統已經給其他進程分配了這樣的分區,則將該分區的描述符返回給申請者,繼之,由申請者把獲得的共享存儲分區連接到本進程上;此后,便可像讀、寫普通存儲器一樣地讀、寫該公用存儲分區。

  2.管道通信系統

  所謂“管道”,是指用於連接一個讀進程和一個寫進程以實現它們之間通信的一個共享文件,又名 pipe 文件。向管道(共享文件)提供輸入的發送進程(即寫進程),以字符流形式將大量的數據送入管道;而接受管道輸出的接收進程(即讀進程),則從管道中接收(讀)數據。由於發送進程和接收進程是利用管道進行通信的,故又稱為管道通信。這種方式首創於UNIX 系統,由於它能有效地傳送大量數據,因而又被引入到許多其它的操作系統中。為了協調雙方的通信,管道機制必須提供以下三方面的協調能力:

  •  互斥,即當一個進程正在對 pipe 執行讀/寫操作時,其它(另一)進程必須等待。
  • 同步,指當寫(輸入)進程把一定數量(如 4 KB)的數據寫入 pipe,便去睡眠等待,直到讀(輸出)進程取走數據后,再把它喚醒。當讀進程讀一空 pipe 時,也應睡眠等待,直至寫進程將數據寫入管道后,才將之喚醒。
  • 確定對方是否存在,只有確定了對方已存在時,才能進行通信。

  3.消息傳遞系統

  在該機制中,進程不必借助任何共享存儲區或數據結構,而是以格式化的消息(Message)為單位,將通信的數據封裝在消息中,並利用操作系統提供的一組通信命令,在進程間進行消息傳遞,完成進程間的數據交換。

  該方式隱藏了通信實現細節,使通信過程對用戶透明化,降低了通信程序設計的復雜性和錯誤率,稱為當前應用最為廣泛的一類進程間通信的機制。例如:在計算機網絡中,又把 message 稱為報文;在當今最為流行的微內核操作系統中,微內核與服務器之間的通信,無一例外地都采用了消息傳遞機制。又由於它能很好地支持多處理機系統、分布式系統和計算機網絡,因此它也成為這些領域最主要的通信工具。消息傳遞系統的通信方式屬於高級通信方式。又因其實現方式的不同而進一步分成直接通信方式和間接通信方式兩種。

  • 直接通信方式。是指發送進程利用 OS 所提供的發送原語,直接把消息發送給目標進程。
  • 間接通信方式。是指發送和接收進程,都通過共享中間實體(稱為郵箱)的方式進行消息的發送和接收,完成進程間的通信。

  4.客戶機-服務器系統

  前面所述的共享內存、消息傳遞等技術,雖然也可以用於實現不同計算機間進程的雙向通信,但是客戶機-服務器系統的通信機制,在網絡環境的各種應用領域已成為當前主流的通信實現機制,其主要的實現方法分為三類:套接字、遠程過程調用和遠程方法調用(調用 RPC 通信協議,通過網絡連接,允許運行於一台主機系統上的進程調用另一台主機系統上的進程)。

 

  五、線程的概述

  在 20 世紀 60 年代中期,人們在設計多道程序 OS 時,引入了進程的概念后,從而解決了單處理機環境下的程序並發執行問題。在此后長達20年的時間里,在多道程序 OS 中一直都是以進程作為能擁有資源和獨立調度(運行)的基本單位的。直到 20 世紀 80 年代中期,人們又提出了比進程更小的能獨立運行的基本單位——線程(Threads),試圖用它來提高系統內程序並發執行的程度,從而可進一步提高系統的吞吐量。特別是在進入 20 世紀 90 年代后,多處理機系統得到迅速發展,線程能比進程更好地提高程序的並行執行程度,充分地發揮多處理機的優越性,因而在近幾年所推出的多處理機 OS 中也都引入了線程,以改善 OS 的性能。

  1.線程的引入

  如果說,在 OS 中引入進程的目的是為了使多個程序能並發執行,以提高資源利用率和系統吞吐量,那么,在操作系統中再引入線程,則是為了減少程序在並發執行時所付出的時空開銷,使 OS 具有更好的並發性。

  (1)進程的兩個基本屬性

進程有兩個基本屬性,正是由於進程有了這兩個基本屬性,才使進程稱為一個能獨立運行的基本單位,從而也就構成了進程並發執行的基礎。

  • 進程是一個可擁有資源的獨立單位,一個進程要能獨立運行,它必須擁有一定的資源,包括用於存放程序正文、數據的磁盤和內存地址空間,以及它在運行時所需要的 I/O 設備、已打開的文件、信號量等;
  • 進程同時又是一個可獨立調度和分派的基本單位,一個進程要能獨立運行,它還必須是一個可獨立調度和分派的基本單位。每個進程在系統中有唯一的 PCB,系統可根據其 PCB 感知進程的存在,也可以根據其 PCB 中的信息,對進程進行調度,還可將端點信息保存在其 PCB 中。反之,再利用進程 PCB 中的信息來恢復進程運行的現場。

  (2)程序並發執行所需付出的時空開銷

  為使程序能並發執行,系統必須進行以下的一系列操作:

  • 創建進程,系統在創建一個進程時,必須為它分配其所必須的、除處理機以外的所有資源,如內存空間、I/O 設備,以及建立相應的 PCB。
  • 撤銷進程,系統在撤銷進程時,又必須先對其所占有的資源執行回收操作,然后再撤銷 PCB。
  • 進程切換,對進程進行上下文切換時,需要保留當前進程的 PCB 環境,設置新選中進程的 PCB 環境,因此須花費不少的處理機時間。

  由於進程是一個資源的擁有者,因而在創建、撤消和切換中,系統必須為之付出較大的時空開銷。這就限制了系統中所設置進程的數目不宜過多,而且進程切換的頻率也不宜過高,這也就限制了並發程度的進一步提高。

  (3)線程-作為調度和分派的基本單位

  如何能使多個程序更好地並發執行,同時又盡量減少系統的開銷,已成為近年來設計操作系統時所追求的重要目標。有不少研究操作系統的學者們想到,若能將進程的上述兩個屬性分開,由操作系統分開處理,亦即對於作為調度和分派的基本單位,不同時作為擁有資源的單位,以做到“輕裝上陣”;而對於擁有資源的基本單位,又不對之進行頻繁的切換。正是在這種思想的指導下,形成了線程的概念。
  隨着 VLSI 技術和計算機體系結構的發展,出現了對稱多處理機(SMP)計算機系統。它為提高計算機的運行速度和系統吞吐量提供了良好的硬件基礎。但要使多個 CPU 很好地協調運行,充分發揮它們的並行處理能力,以提高系統性能,還必須配置性能良好的多處理機 OS。但利用傳統的進程概念和設計方法,已難以設計出適合於 SMP 結構的計算機系統的 OS。其最根本的原因是進程“太重”,致使實現多處理機環境下的進程調度、分派和切換時,都需花費較大的時間和空間開銷。如果在 OS 中引入線程,以線程作為調度和分派的基本單位,則可以有效地改善多處理機系統的性能。因此,一些主要的 OS(UNIX、OS/2、Windows)廠家都又進一步對線程技術做了開發,使之適用於 SMP 的計算機系統。

  

  2.線程與進程的比較

  線程具有許多傳統進程所具有的特征,所以又稱為輕型進程(Light-Weight Process)或進程元。相應地,把傳統進程稱為重型進程(Heavy-Weight Process),傳統進程相當於只有一個線
程的任務。在引入了線程的操作系統中,通常一個進程都擁有若干個線程,至少也有一個線程。下面我們從調度性、並發性、系統開銷和擁有資源等方面對線程和進程進行比較。

  (1)調度的基本單位

  在傳統的 OS 中,進程是作為獨立調度和分派的基本單位,因而進程是能獨立運行的基本單位。在每次被調度時,都需要進行上下文切換,開銷較大。而在引入線程的操作系統中,則把線程作為調度和分派的基本單位,因而線程是能獨立運行的基本單位。而進程作為資源擁有的基本單位,把傳統進程的兩個屬性分開,使線程基本上不擁有資源,這樣線程便能輕裝前進,從而可顯著地提高系統的並發程度。

  當線程切換時,僅需保存和設置少量寄存器內容,切換代價遠低於進程。在同一進程中,線程的切換不會引起進程的切換,但從一個進程中的線程切換到另一個進程中的線程時,將會引起進程的切換。

(2)並發性

  在引入線程的 OS 中,不僅進程之間可以並發執行,而且在一個進程中的多個線程之間亦可並發執行,甚至還允許在一個進程中所有線程都能並發執行。同樣,不同進程中的線程也能並發執行。這使得 OS 具有更好的並發性,從而能更加有效地提高系統資源的利用率和吞吐量。例如,在文字處理器進程中可以設置三個線程:第一個線程用於顯示文字和圖形,第二個線程從鍵盤讀入數據,第三個線程在后台進行拼寫和語法檢查。又如,在網頁瀏覽器中,可以設置一個線程來顯示圖像或文本,再設置一個線程用於從網絡中接收數據。

  此外,有的應用程序需要執行多個相似的任務。例如,一個網絡服務器經常會接到許多客戶的請求,如果仍采用傳統的單線程的進程來執行該任務,則每次只能為一個客戶服務。但如果在一個進程中可以設置多個線程,將其中一個專用於監聽客戶的請求,則每當有一個客戶請求時,便立即創建一個線程來處理該客戶的請求。

  (3)擁有資源

  進程可以擁有資源,並作為系統中擁有資源的一個基本單位。然而,線程本身不擁有系統資源,而是僅有一點必不可少的、能保證獨立運行的資源。比如,在每個線程中都應具有一個用於控制線程運行的線程控制塊 TCB、用於只是被執行指令序列的程序計數器、保留局部變量、少數狀態參數和返回地址等的一組寄存器和堆棧。

  線程除了擁有自己的少量資源外,還允許多個線程共享該進程所擁有的資源,這首先表現在:屬於統一進程的所有線程都具有相同的地址空間,這意味着,線程可以訪問該地址空間中的每一個虛地址;此外,還可以訪問進程中所擁有的資源,如已經打開的文件、定時器、信號量機構等的內存空間和它所申請到的 I/O 設備等。

  (4)獨立性

  在同一進程的不同線程之間的獨立性要比不同進程之間到的獨立性低得多,這是因為,為防止進程之間彼此干擾和破壞,每個進程都擁有一個獨立的地址空間和其他資源,除了共享全局變量外,不允許其他進程的訪問。但是同一進程中的不同線程往往是為了提高並發性以及進行相互之間的合作而創建的,它們共享進程的內存地址空間和其他資源。如每個線程都可以訪問它們所屬進程地址空間中的所有地址,如一個線程的堆棧可以被其他線程讀/寫,甚至可以完全清除。由一個線程打開的文件可以供其他線程讀/寫。

  (5)系統開銷

  在創建或撤銷進程時,系統都要為之分配和回收進程控制塊、分配或回收其他資源,如內存空間和 I/O 設備等。OS 為此所付出的開銷,明顯大於線程創建或撤銷時所付出的開銷。類似地,在進程切換時,涉及到進程上下文的切換,而線程的切換代價也遠低於進程的。例如,在 Solaris 2 OS 中,線程的創建要比進程的創建塊 30 倍,而線程上下文切換要比進程上下文切換塊 5 倍。此外,由於一個進程中的多個線程具有相同的地址空間,線程之間的同步和通信也比進程的簡單。因此,在一些 OS 中,線程的切換、同步和通信都無需操作系統內核的干預。

  (6)支持多處理機系統

  在多處理機系統中,對於傳統的進程,即單線程進程,不管有多少個處理機,該進程只能運行在一個處理機上。但對於多線程進程,就可以將一個進程中的多個線程分配到多個處理機上,使它們並行執行,這無疑將加速進程的完成。因此,現代多處理機 OS 都無一例外地引入了多線程。

 

  3.線程的狀態和線程控制塊

  (1)線程運行的三個狀態

  • 執行狀態,表示線程已獲得處理機而正在運行。
  • 就緒狀態,指線程已具備了各種執行條件,只需再獲得 CPU 便可立即執行;
  • 阻塞狀態,指線程在執行中因某事件受阻而處於暫停狀態,例如,當一個線程執行從鍵盤讀入數據的系統調用時,該線程就被阻塞。

    線程狀態之間的轉換和進程狀態之間的轉換是一樣的。

  (2)線程控制塊 TCB

  如同每個進程有一個進程控制塊一樣,系統也為每個線程配置了一個線程控制塊 TCB,將所有用於控制和管理線程的信息記錄在線程控制塊中。線程控制塊通常有這樣幾項:

  • ①線程標識符,為每個線程賦予一個唯一的線程標識符
  • ②一組寄存器,包括程序計數器 PC、狀態寄存器和通用寄存器的內容
  • ③線程運行狀態,用於描述線程正處於何種運行狀態;
  • ④優先級,描述線程執行的優先程度
  • ⑤線程專有存儲區,用於線程切換時存放現場保護信息,和與該線程相關的統計信息等
  • ⑥信號屏蔽,即對某些信號加以屏蔽
  • ⑦堆棧指針,在線程運行時,經常會進行過程調用,而過程的調用通常會出現多重嵌套的情況,這樣,就必須將每次過程調用中所使用的局部變量以及返回地址保存起來。為此,應為每個線程設置一個堆棧,用它來保存局部變量和返回地址。相應地,在 TCB 中,也必須設置兩個指向堆棧的指針:指向用戶自己對戰的指針和指向核心棧的指針。前者是指當線程運行在用戶態是,使用用戶自己的用戶棧來保存局部變量和返回地址,后者是指當線程運行在核心態時使用系統的核心棧。

  (3)多線程 OS 中的進程屬性

通常在多線程 OS 中的進程都包含了多個線程,並為它們提供資源。OS 支持在一個進程中的多個線程能並發執行,但此時的進程就不再作為一個執行的實體。多線程 OS 中進程的屬性:

  • 進程是一個可擁有資源的基本單位。在多線程 OS 中,進程仍是作為系統資源分配的基本單位,任一進程中所擁有的資源都包括:受到分別保護的用戶地址空間、用於實現進程(線程)間同步和通信的機制、已打開的文件和已申請到的 I/O 設備,以及一張由核心進程維護的地址映射表,該表用於實現用戶程序的邏輯地址到其內存物理地址的映射。
  • 多個線程可並發執行。通常,一個進程都含有若干個相對獨立的線程,其數目可多可少,但至少也要有一個線程。由進程為這些(個)線程提供資源及運行環境,使它們能並發執行。在 OS 中的所有線程都只能屬於某一個特定進程。實際上,現在把傳統進程執行方法稱為單線程方法。如傳統的 UNIX 系統能支持多用戶進程,但只支持單線程方法。反之,將每個進程支持多個線程執行的方法稱為多線程方法。如 Java 的運行環境是單進程多線程的,而Windows 2000,Solaris、Mach 等采用的則是多進程多線程的方法。
  • 進程已不是一個可執行的實體。在多線程 OS 中,是把線程作為獨立運行(或稱調度)的基本單位,而此時的進程已不再是一個可執行的實體。雖然如此,進程仍具有與執行相關的狀態。例如,所謂進程處於“執行”狀態,實際上是指該進程中的某線程正在執行。此外,對進程所施加的與進程狀態有關的操作,也對其線程起作用。例如,在把某個進程掛起時,該進程中的所有線程也都將被掛起;又如,在把某進程激活時,屬於該進程的所有線程也都將被激活。

  

  六、線程的實現

  1.線程的實現方式

  (1)內核支持線程 KST(Kernel Supported Threads)

  在 OS 中的所有進程,無論是系統進程還是用戶進程,都是在操作系統內核的支持下運行的,是與內核緊密相關的。而內核支持線程 KST(Kernel Supported Threads),也都同樣是在內核的支持下運行的,它們的創建、 阻塞、撤銷和切換等,也都是在內核空間實現的。為了對內核線程進行控制和管理,在內核空間也為每一個內核線程設置了一個線程控制塊,內核根據該控制塊而感知某線程的存在,並對其加以控制。當前大多數 OS 都支持內核支持線程。

  這種線程實現方式的四個優點:

  • 在多處理器系統中,內核能夠同時調度同一進程中多個線程並行執行;
  • 如果進程中的一個線程被阻塞了,內核可以調度該進程中的其它線程占有處理器運行,也可以運行其它進程中的線程;
  • 內核支持線程具有很小的數據結構和堆棧,線程的切換比較快,切換開銷小;
  • 內核本身也可以采用多線程技術,可以提高系統的執行速度和效率。

    這種線程實現方式的缺點:

  • 對於用戶的線程切換而言,其模式切換的開銷較大,在同一個進程中,從一個線程切換到另一個線程時,需要從用戶態轉到內核態進行,這是因為用戶進程的線程在用戶態運行,而線程調度和管理是在內核實現的,系統開銷較大。

  (2)用戶級線程 ULT(User Level Threads)

  用戶級線程是在用戶空間中實現的。對線程的創建、撤消、線程之間的同步與通信等功能,都無須利用系統調用來實現,即用戶級線程是與內核無關的。在一個系統中的用戶級線程的數目可以達到數百個至數千個。由於這些線程的任務控制塊都是設置在用戶空間,而線程所執行的操作也無須內核的幫助,因而內核完全不知道用戶級線程的存在。

  值得說明的是,對於設置了用戶級線程的系統,其調度仍是以進程為單位進行的。在采用輪轉調度算法時,各個進程輪流執行一個時間片,這對諸進程而言似乎是公平的。但假如在進程 A 中包含了一個用戶級線程,而在另一個進程 B 中含有 100 個用戶級線程,這樣,進程 A 中線程的運行時間將是進程 B 中各線程運行時間的 100 倍;相應地,其速度要快上 100 倍。

  假如系統中設置的是內核支持線程,則調度便是以線程為單位進行的。在采用輪轉法調度時,是各個線程輪流執行一個時間片。同樣假定進程 A 中只有一個內核支持線程,而在進程 B 中有 100 個內核支持線程。此時進程 B 可以獲得的 CPU 時間是進程 A 的 100 倍,且進程 B 可使 100 個系統調用並發工作。

  使用用戶級線程實現方式有很多優點:

  • 線程切換不需要轉換到內核空間,對一個進程而言,其所有線程的管理數據結構均在該進程的用戶空間中,管理線程切換的線程庫也在用戶地址空間運行。因此,進程不必切換到內核方式來做線程管理,從而節省了模式切換的開銷,也節省了內核的寶貴資源。
  • 調度算法可以是進程專用的。在不干擾操作系統調度的情況下,不同的進程可以根據自身需要,選擇不同的調度算法對自己的線程進行管理和調度,而與操作系統的低級調度算法是無關的。
  • 用戶級線程的實現與操作系統平台無關,因為對於線程管理的代碼是在用戶程序內的,屬於用戶程序的一部分,所有的應用程序都可以對之進行共享。因此,用戶級線程甚至可以在不支持線程機制的操作系統平台上實現。

用戶級線程實現方式的主要缺點在於如下兩個方面:

  • 系統調用的阻塞問題。在基於進程機制的操作系統中,大多數系統調用將阻塞進程,因此,當線程執行一個系統調用時,不僅該線程被阻塞,而且進程內的所有線程都會被阻塞。而在內核支持線程方式中,則進程中的其它線程仍然可以運行。
  • 在單純的用戶級線程實現方式中,多線程應用不能利用多處理機進行多重處理的優點。內核每次分配給一個進程的僅有一個 CPU,因此進程中僅有一個線程能執行,在該線程放棄 CPU 之前,其它線程只能等待。

  (3)組合方式

  有些操作系統把用戶級線程和內核支持線程兩種方式進行組合,提供了組合方式ULT/KST 線程。在組合方式線程系統中,內核支持多 KST 線程的建立、調度和管理,同時,也允許用戶應用程序建立、調度和管理用戶級線程。一些內核支持線程對應多個用戶級線程,程序員可按應用需要和機器配置對內核支持線程數目進行調整,以達到較好的效果。組合方式線程中,同一個進程內的多個線程可以同時在多處理器上並行執行,而且在阻塞一個線程時,並不需要將整個進程阻塞。所以,組合方式多線程機制能夠結合 KST 和 ULT兩者的優點,並克服了其各自的不足。

 

  2.線程的實現

  不論是進程還是線程,都必須直接或間接地取得內核的支持。由於內核支持線程可以直接利用系統調用為它服務,故線程的控制相當簡單;而用戶級線程必須借助於某種形式的中間系統的幫助方能取得內核的服務,故在對線程的控制上要稍復雜些。

  (1)內核支持線程的實現

  在僅設置了內核支持線程的 OS 中,一種可能的線程控制方法是,系統在創建一個新進程時,便為它分配一個任務數據區 PTDA(Per Task Data Area),其中包括若干個線程控制塊TCB 空間,在每一個 TCB 中可保存線程標識符、優先級、線程運行的 CPU狀態等信息。雖然這些信息與用戶級線程 TCB 中的信息相同,但現在卻是被保存在內核空間中。

  每當進程要創建一個線程時,便為新線程分配一個TCB,將有關信息填入該 TCB 中,並為之分配必要的資源,如為線程分配數百至數千個字節的棧空間和局部存儲區,於是新創建的線程便有條件立即執行。當 PTDA中的所有 TCB 空間已用完,而進程又要創建新的線程時,只要其所創建的線程數目未超過系統的允許值(通常為數十至數百個),系統可再為之分配新的 TCB 空間;在撤消一個線程時,也應回收該線程的所有資源和 TCB。可見,內核支持線程的創建、 撤消均與進程的相類似。在有的系統中為了減少創建和撤消一個線程時的開銷,在撤消一個線程時,並不立即回收該線程的資源和 TCB,當以后再要創建一個新線程時,便可直接利用已被撤消但仍保持有資源和 TCB 的線程作為新線程。

  內核支持線程的調度和切換與進程的調度和切換十分相似,也分搶占式方式和非搶占方式兩種。在線程的調度算法上,同樣可采用時間片輪轉法、優先權算法等。當線程調度選中一個線程后,便將處理機分配給它。當然,線程在調度和切換上所花費的開銷,要比進程的小得多。

  (2)用戶級線程的實現

  用戶級線程是在用戶空間實現的。所有的用戶級線程都具有相同的結構,它們都運行在一個中間系統的上面。當前有兩種方式實現的中間系統,即運行時系統和內核控制線程。

  • 運行時系統。①所謂“運行時系統”,實質上是用於管理和控制線程的函數(過程)的集合,其中包括用於創建和撤消線程的函數、 線程同步和通信的函數以及實現線程調度的函數等。正因為有這些函數,才能使用戶級線程與內核無關。運行時系統中的所有函數都駐留在用戶空間,並作為用戶級線程與內核之間的接口。②在傳統的 OS 中,進程在切換時必須先由用戶態轉為核心態,再由核心來執行切換任務;而用戶級線程在切換時則不需轉入核心態,而是由運行時系統中的線程切換過程來執行切換任務。該過程將線程的 CPU 狀態保存在該線程的堆棧中,然后按照一定的算法選擇一個處於就緒狀態的新線程運行,將新線程堆棧中的 CPU 狀態裝入到 CPU 相應的寄存器中,一旦將棧指針和程序計數器切換后,便開始了新線程的運行。由於用戶級線程的切換無需進入內核,且切換操作簡單,因而使用戶級線程的切換速度非常快。③不論在傳統的 OS 中,還是在多線程 OS 中,系統資源都是由內核管理的。在傳統的OS 中,進程是利用 OS 提供的系統調用來請求系統資源的,系統調用通過軟中斷(如 trap)機制進入 OS 內核,由內核來完成相應資源的分配。用戶級線程是不能利用系統調用的。當線程需要系統資源時,是將該要求傳送給運行時系統,由后者通過相應的系統調用來獲得系統資源的。
  • 內核控制線程。①這種線程又稱為輕型進程 LWP(Light Weight Process)。每一個進程都可擁有多個 LWP,同用戶級線程一樣,每個 LWP 都有自己的數據結構(如 TCB),其中包括線程標識符、優先級、狀態,另外還有棧和局部存儲區等。它們也可以共享進程所擁有的資源。LWP 可通過系統調用來獲得內核提供的服務,這樣,當一個用戶級線程運行時,只要將它連接到一個LWP 上,此時它便具有了內核支持線程的所有屬性。這種線程實現方式就是組合方式。②在一個系統中的用戶級線程數量可能很大,為了節省系統開銷,不可能設置太多的LWP,而把這些 LWP 做成一個緩沖池,稱為“線程池”。用戶進程中的任一用戶線程都可以連接到 LWP 池中的任何一個 LWP 上。為使每一用戶級線程都能利用 LWP 與內核通信,可以使多個用戶級線程多路復用一個 LWP,但只有當前連接到 LWP 上的線程才能與內核通信,其余進程或者阻塞,或者等待 LWP。而每一個 LWP 都要連接到一個內核級線程上,這樣,通過 LWP 可把用戶級線程與內核線程連接起來,用戶級線程可通過 LWP 來訪問內核,但內核所看到的總是多個 LWP 而看不到用戶級線程。亦即,由 LWP 實現了在內核與用戶級線程之間的隔離,從而使用戶級線程與內核無關。③當用戶級線程不需要與內核通信時,並不需要 LWP;而當要通信時,便需借助於 LWP,而且每個要通信的用戶級線程都需要一個 LWP。例如,在一個任務中,如果同時有 5 個用戶級線程發出了對文件的讀、寫請求,這就需要有 5 個 LWP 來予以幫助,即由 LWP 將對文件的讀、寫請求發送給相應的內核級線程,再由后者執行具體的讀、寫操作。如果一個任務中只有 4 個 LWP,則只能有 4 個用戶級線程的讀、寫請求被傳送給內核線程,余下的一個用戶級線程必須等待。④在內核級線程執行操作時,如果發生阻塞,則與之相連接的多個 LWP 也將隨之阻塞,進而使連接到 LWP 上的用戶級線程也被阻塞。如果進程中只包含了一個 LWP,此時進程也應阻塞。這種情況與前述的傳統 OS 一樣,在進程執行系統調用時,該進程實際上是阻塞的。但如果在一個進程中含有多個 LWP,則當一個 LWP 阻塞時,進程中的另一個 LWP 可繼續執行;即使進程中的所有 LWP 全部阻塞,進程中的線程也仍然能繼續執行,只是不能再去訪問內核。
  •  

  3.線程的創建和終止

  如同進程一樣,線程也是具有生命期的,它由創建而產生,由調度而執行,由終止而消亡。相應的,在 OS 中也就有用於創建線程的函數(或系統調用)和用於終止線程的函數(或系統調用)。

  (1)線程的創建

  應用程序在啟動時,通常僅有一個線程在執行,該線程被人們稱為“初始化線程”。它的主要功能是用於創建新線程。在創建新線程時,需要利用一個線程創建函數(或系統調用),並提供相應的參數,如指向線程主程序的入口指針、堆棧的大小,以及用於調度的優先級等。在線程創建函數執行完后,將返回一個線程標識符供以后使用。

  (2)線程的終止

  終止線程的方式有兩種:一種是在線程完成了自己的工作后自願退出;另一種是線程在運行中出現異常情況或由於某種原因而被其它線程強行終止,由終止線程通過調用相應的函數(或系統調用)對它執行終止操作。但有些線程(主要是系統線程),在它們一旦被建立起來之后,便一直運行下去而不再被終止。在大多數的 OS 中,線程被終止后並不立即釋放它所占有的資源,只有當進程中的其它線程執行了分離函數后,被終止的線程才與資源分離,此時的資源才能被其它線程利用。
  雖已被終止但尚未釋放資源的線程,仍可以被需要它的線程所調用,以使被終止線程重新恢復運行。為此,調用者線程須調用一條被稱為“等待線程終止”的連接命令,來與該線程進行連接。如果在一個調用者線程調用“等待線程終止”的連接命令試圖與指定線程相連接時,若指定線程尚未被終止,則調用連接命令的線程將會阻塞,直至指定線程被終止后才能實現它與調用者線程的連接並繼續執行;若指定線程已被終止,則調用者線程不會被阻塞而是繼續執行。

 

4.線程間的同步和通信

  為使系統中的多線程能有條不紊地運行,在系統中必須提供用於實現線程間同步和通信的機制。為了支持不同頻率的交互操作和不同程度的並行性,在多線程 OS 中通常提供多種同步機制,如互斥鎖、條件變量、計數信號量以及多讀、單寫鎖等。

(1)互斥鎖

  互斥鎖是一種比較簡單的、用於實現線程間對資源互斥訪問的機制。由於操作互斥鎖的時間和空間開銷都較低,因而較適合於高頻度使用的關鍵共享數據和程序段。互斥鎖可以有兩種狀態,即開鎖(unlock)和關鎖(lock)狀態。相應地,可用兩條命令(函數)對互斥鎖進行操作。其中的關鎖 lock 操作用於將 mutex 關上,開鎖操作 unlock 則用於打開 mutex。
  當一個線程需要讀/寫一個共享數據段時,線程首先應為該數據段所設置的 mutex 執行關鎖命令。命令首先判別 mutex 的狀態,如果它已處於關鎖狀態,則試圖訪問該數據段的線程將被阻塞;而如果 mutex 是處於開鎖狀態,則將 mutex 關上后便去讀/寫該數據段。在線程完成對數據的讀/寫后,必須再發出開鎖命令將 mutex 打開,同時還須將阻塞在該互斥鎖上的一個線程喚醒,其它的線程仍被阻塞在等待 mutex 打開的隊列上。
  另外,為了減少線程被阻塞的機會,在有的系統中還提供了一種用於 mutex 上的操作命令 Trylock。當一個線程在利用 Trylock 命令去訪問 mutex 時,若 mutex 處於開鎖狀態,Trylock 將返回一個指示成功的狀態碼;反之,若 mutex 處於關鎖狀態,則 Trylock 並不會阻塞該線程,而只是返回一個指示操作失敗的狀態碼。

(2)條件變量

  在許多情況下,只利用 mutex 來實現互斥訪問可能會引起死鎖,我們通過一個例子來說明這一點。有一個線程在對 mutex 1 執行關鎖操作成功后,便進入一臨界區 C,若在臨界區內該線程又須訪問某個臨界資源 R,同樣也為 R 設置另一互斥鎖 mutex 2。假如資源 R 此時正處於忙碌狀態,線程在對 mutex 2 執行關鎖操作后必將被阻塞,這樣將使 mutex 1 一直保持關鎖狀態;如果保持了資源 R 的線程也要求進入臨界區 C,但由於 mutex 1 一直保持關鎖狀態而無法進入臨界區,這樣便形成了死鎖。為了解決這個問題便引入了條件變量。每一個條件變量通常都與一個互斥鎖一起使用,亦即,在創建一個互斥鎖時便聯系着一個條件變量。單純的互斥鎖用於短期鎖定,主要是用來保證對臨界區的互斥進入。而條件變量則用於線程的長期等待,直至所等待的資源成為可用的資源。
  如何利用互斥鎖和條件變量來實現對資源 R 的訪問。線程首先對 mutex執行關鎖操作,若成功便進入臨界區,然后查找用於描述該資源狀態的數據結構,以了解資源的情況。只要發現所需資源 R 正處於忙碌狀態,線程便轉為等待狀態,並對 mutex 執行開鎖操作后,等待該資源被釋放;若資源處於空閑狀態,表明線程可以使用該資源,於是將該資源設置為忙碌狀態,再對 mutex 執行開鎖操作。

  原來占有資源 R 的線程在使用完該資源后,便按照右半部分的描述釋放該資源,其中的 wakeup(condition variable)表示去喚醒在指定條件變量上等待的一個或多個線程。在大多數情況下,由於所釋放的是臨界資源,此時所喚醒的只能是在條件變量上等待的某一個線程,其它線程仍繼續在該隊列上等待。但如果線程所釋放的是一個數據文件,該文件允許多個線程同時對它執行讀操作。在這種情況下,當一個寫線程完成寫操作並釋放該文件后,如果此時在該條件變量上還有多個讀線程在等待,則該線程可以喚醒所有的等待線程。

(3)信號量機制

  前面所介紹的用於實現進程同步的最常用工具——信號量機制,也可用於多線程 OS中,實現諸線程或進程之間的同步。為了提高效率,可為線程和進程分別設置相應的信號量。

  • 私用信號量(private samephore)。當某線程需利用信號量來實現同一進程中各線程之間的同步時,可調用創建信號量的命令來創建一私用信號量,其數據結構存放在應用程序的地址空間中。私用信號量屬於特定的進程所有,OS 並不知道私用信號量的存在,因此,一旦發生私用信號量的占用者異常結束或正常結束,但並未釋放該信號量所占有空間的情況時,系統將無法使它恢復為 0(空),也不能將它傳送給下一個請求它的線程。
  • 公用信號量(public semaphort)。公用信號量是為實現不同進程間或不同進程中各線程之間的同步而設置的。由於它有着一個公開的名字供所有的進程使用,故而把它稱為公用信號量。其數據結構是存放在受保護的系統存儲區中,由 OS 為它分配空間並進行管理,故也稱為系統信號量。如果信號量的占有者在結束時未釋放該公用信號量,則 OS 會自動將該信號量空間回收,並通知下一進程。可見,公用信號量是一種比較安全的同步機制。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM