【操作系統】進程管理


  程序的順序執行如下圖,其中I代表輸入,C代表計算,P代表打印。程序順序執行時的特征有順序性、封閉性(獨占全機資源)、可再現性

   程序的並發執行如下圖,其中I代表輸入,C代表計算,P代表打印。輸入程序在輸入第一個程序后,在計算程序對該程序進行計算的同時,可由輸入程序再輸入第二個程序,從而使第一個程序的計算操作可與第二個程序的輸入操作並發執行。程序並發執行的特征有間斷性、失去封閉性、不可再現性

進程基本概念

  進程是程序的一次執行過程。由程序段、相關的數據段PCB(進程控制塊)三部分便構成了進程實體(或稱為進程映像)。進程的特征有動態性(它由創建而產生,由調度而執行,由撤消而消亡)、並發性獨立性(進程是一個獨立運行、分配資源和調度的基本單位)、異步性

  程的狀態

  • 就緒態:進程已獲得除CPU外的所有必要資源。系統中通常有一個就緒隊列。
  • 執行態:程序正在執行。在單處理機系統中,只有一個進程處於執行狀態;在多處理機系統中,則有多個進程處於執行狀態。
  • 阻塞態:正在執行的進程由於發生某事件而暫時無法繼續執行時,便放棄CPU而處於暫停狀態。致使進程阻塞的典型事件有:請求I/O,申請緩沖空間等。系統會有一個或多個請求資源阻塞隊列。

  • 創建態:進程正在創建。創建一個進程一般要通過兩個步驟:首先,為一個新進程創建PCB,並填寫必要的管理信息;其次,把該進程轉入就緒狀態並插入就緒隊列之中。
  • 終止態:進程的終止也要通過兩個步驟:首先等待操作系統進行善后處理,然后將其PCB清零,並將PCB 空間返還系統。

   引入掛起

   掛起(suspend) vs.  阻塞(pend)

  參考:https://www.cnblogs.com/jason-liu-blogs/archive/2012/12/19/2825202.html

  • 掛起的掛起和恢復是一種主動行為,而阻塞則是一種被迫自發(迫不得已的自己阻塞自己)行為,是在等待事件或資源時任務的表現。
  • 任務調度是操作系統來實現的,任務調度時,直接忽略掛起狀態的任務但是會顧及處於阻塞下的任務,當阻塞下的任務等待的資源就緒后,就可以轉為就緒了。可以這樣理解,只要是掛起狀態,操作系統就不在管理這個任務了。
  操作系統中睡眠、阻塞、掛起的區別形象解釋:
  掛起的意思就是你對主動對雇工說:“你睡覺去吧,用着你的時候我主動去叫你,然后接着干活”。
  睡眠的意思就是你主動對雇工說:“你睡覺去吧,某時某刻過來報到,然后接着干活”。
  阻塞的意思就是,你突然發現,你的雇工不知道在什么時候沒經過你允許,自己睡覺呢,但是你不能怪雇工,肯定你這個雇主沒注意,本來你讓雇工掃地,結果掃帚被偷了或被鄰居家借去了,你又沒讓雇工繼續干別的活,他就只好睡覺了。至於掃帚回來后,雇工會不會知道,會不會繼續干活,你不用擔心,雇工一旦發現掃帚回來了,他就會自己去干活的。

  進程控制塊(PCB)

  為了描述和控制進程的運行,系統為每個進程定義了一個數據結構——進程控制塊,它是進程實體的一部分,記錄了操作系統所需的、用於描述進程的當前情況以及控制進程運行的全部信息。OS是根據PCB來對並發執行的進程進行控制和管理的。PCB是進程存在的惟一標志

  PCB中所含信息:

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

  PCB的組織方式有鏈接方式和索引方式

進程控制

  進程控制是進程管理中最基本的功能,負責進程的狀態轉換。進程控制一般是由OS的內核中的原語來實現的。原語是由若干條指令組成的,用於完成一定功能的一個過程。它與一般過程的區別在於:它們是原子操作,即一個操作中的所有動作要么全做,要么全不做,是一個不可分割的基本單位,因此,在執行過程中不允許被中斷。原子操作在管態下執行,常駐內存。

  子進程與父進程。子進程可以繼承父進程所擁有的資源,例如,繼承父進程打開的文件,繼承父進程所分配到的緩沖區等。當子進程被撤消時,應將其從父進程那里獲得的資源歸還給父進程。此外,在撤消父進程時,也必須同時撤消其所有的子進程。  

  進程創建

  引起創建的事件:

  • (1) 用戶登錄。用戶登錄后系統將為該終端建立一個進程,並把它插入就緒隊列中。
  • (2) 作業調度。
  • (3) 提供服務。當運行中的用戶程序提出某種請求后,系統將專門創建一個進程來提供用戶所需要的服務。
  • (4) 應用請求。在上述三種情況下,都是由系統內核為它創建一個新進程;而第4 類事件則是基於應用進程的需求,由它自己創建一個新進程。

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

  1. 申請空白PCB。為新進程申請獲得惟一的數字標識符,並從PCB 集合中索取一個空白PCB。
  2. 為新進程分配資源。為新進程的程序和數據以及用戶棧分配必要的內存空間。
  3. 初始化進程控制塊。PCB 的初始化包括:① 初始化標識信息,將系統分配的標識符和父進程標識符填入新PCB中;② 初始化處理機狀態信息,使程序計數器指向程序的入口地址,使棧指針指向棧頂;③ 初始化處理機控制信息,將進程的狀態設置為就緒狀態或靜止就緒狀態,對於優先級,通常是將它設置為最低優先級,除非用戶以顯式方式提出高優先級要求。
  4. 將新進程插入就緒隊。如果進程就緒隊列能夠接納新進程,便將新進程插入就緒隊列。

  進程終止

  引起終止的事件:

  • (1)正常結束。
  • (2)異常結束。越界錯誤、保護錯、非法指令、 特權指令錯、運行超時、等待超時、算術運算錯、I/O 故障。
  • (3)外界干預。操作員或操作系統干預、父進程請求、父進程終止。

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

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

  進程阻塞與喚醒

  引起阻塞與喚醒的事件:

  • (1)請求系統服務 。
  • (2)啟動某種操作。
  • (3)新數據尚未到達。
  • (4)無新工作可做。

  阻塞過程:正在執行的進程,當發現上述某事件時,由於無法繼續執行,於是進程便通過調用阻塞原語block()把自己阻塞。先立即停止執行,把進程控制塊中的現行狀態由“執行”改為“阻塞”,並將PCB插入阻塞隊列。后轉調度程序進行重新調度,將處理機分配給另一就緒進程並進行切換。

  喚醒過程:當被阻塞進程所期待的事件出現時,則由有關進程(比如用完並釋放了該I/O 設備的進程)調用喚醒原語wakeup( ),將等待該事件的進程喚醒。首先把被阻塞的進程從等待該事件的阻塞隊列中移出,將其PCB中的現行狀態由阻塞改為就緒,然后再將該PCB插入到就緒隊列中。

  進程掛起與激活

  引起掛起的事件:

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

  掛起過程:當出現了引起進程掛起的事件時,系統將利用掛起原語suspend( )將指定進程或處於阻塞狀態的進程掛起。首先檢查被掛起進程的狀態,若處於活動就緒狀態,便將其改為靜止就緒; 對於活動阻塞狀態的進程,則將之改為靜止阻塞。

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

進程同步

  進程同步的主要任務是對多個相關進程在執行次序上進行協調,以使並發執行的諸進程之間能有效地共享資源和相互合作,從而使程序的執行具有可再現性。共享資源形成進程間間接制約關系,相互合作形成進程間直接制約關系。

  生產者-消費者問題,counter就是一個臨界資源變量,因為但不互斥訪問它時會出現問題。

 

  臨界資源有硬件臨界資源和軟件臨界資源,多個進程必須互斥的對它進行訪問。把在每個進程中訪問臨界資源的那段代碼稱為臨界區

  一個進程的代碼段可以划分如下:

   同步機制遵循准則:

  • 空閑讓進
  • 忙則等待
  • 有限等待。應保證有限時間內可以進入臨界區,避免死等。
  • 讓權等待。當不能進去臨界區時,應釋放CPU,避免忙等。

   進程同步的機制有:信號量機制、管程機制。

  信號量機制

  介紹了整型信號量,記錄型信號量,AND型信號量,信號量集。

  整型信號量為一個用於表示可用資源數目的整型量S,除了初始化以外,僅支持兩個原子操作,保證了操作不能被中斷,wait(S)和signal(S),也稱為P和V操作。這種機制沒有遵循讓權等待,會出現忙等

  記錄型信號量由於它采用了記錄型的數據結構而得名的,它解決了忙等問題。如果S.value的初值為1,表示只允許一個進程訪問臨界資源此時的信號量轉化為互斥信號量,用於進程互斥。

  相應的wait(S)和signal(S)操作:

   AND型信號量應用於一個進程需要先獲得兩個或更多的共享資源后方能執行其任務的場景。假定現有兩個進程A和B,他們都要求訪問共享數據D 和E。當然,共享數據都應作為臨界資源。為此,可為

這兩個數據分別設置用於互斥的信號量Dmutex 和Emutex,並令它們的初值都是1。但是會出現死鎖問題。

 

  最后,進處於僵持狀態。在無外力作用下,兩者都將無法從僵持狀態中解脫出來。我們稱此時的進已進入死鎖狀態。當進程同時要求的共享資源愈多時,發生進程死鎖的可能性也就愈大。

  AND 同步機制的基本思想是:將進程在整個運行過程中需要的所有資源,一次性全部地分配給進程,待進程使用完后再一起釋放。只要尚有一個資源未能分配給進程,其它所有可能為之分配的資源也不分配給它。亦即,對若干個臨界資源的分配,采取原子操作方式:要么把它所請求的資源全部分配到進程,要么一個也不分配

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

   利用信號量實現進程互斥,解決共享資源問題為使多個進程能互斥地訪問某臨界資源,只須為該資源設置一互斥信號量mutex,並設其初始值為1,然后將各進程訪問該資源的臨界區CS置於wait(mutex)和signal(mutex)操作之間即可。

   利用信號量實現前趨關系,解決相互合作問題設有兩個並發執行的進程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;

  如下一個例子,應該設置a,b,c,d,e,f,g信號量,並初始化為0。

  管程機制

  信號量機制要求每個要訪問臨界資源的進程都必須自備同步操作wait(S)和signal(S),這就使大量的同步操作分散在各個進程中。這不僅給系統的管理帶來了麻煩,而且還會因同步操作的使用不當而導致系統死鎖。故引入進程同步工具——管程。

   代表共享資源的數據結構,以及由對該共享數據結構實施操作的一組過程所組成的資源管理程序,共同構成了一個操作系統的資源管理模塊,我們稱之為管程。管程被請求和釋放資源的進程所調用。管程由四部分組成:① 管程的名稱;② 局部於管程內部的共享數據結構說明;③ 對該數據結構進行操作的一組過程;④ 對局部於管程內部的共享數據設置初始值的語句。所有進程要訪問臨界資源時,都必須經過管程(相當於通過圍牆的門)才能進入,而管程每次只准許一個進程進入管程,從而實現了進程互斥

 

 管程和進程的區別:

  • (1) 二者都定義了數據結構,但進程定義的是私有數據結構PCB,管程定義的是公共數據結構,如消息隊列等;
  • (2) 二者都存在對各自數據結構上的操作,但進程是由順序程序執行有關的操作,而管程主要是進行同步操作和初始化操作;
  • (3) 設置進程的目的在於實現系統的並發性,而管程的設置則是解決共享資源的互斥使用問題;
  • (4) 進程通過調用管程中的過程對共享數據結構實行操作,該過程就如通常的子程序一樣被調用,因而管程為被動工作方式,進程則為主動工作方式;
  • (5) 進程之間能並發執行,而管程則不能與其調用者並發;
  • (6) 進程具有動態性,而管程則是操作系統中的一個資源管理模塊,供進程調用。

  當一個進程調用了管程,在管程中時被阻塞或掛起,直到阻塞或掛起的原因解除,而在此期間,如果該進程不釋放管程,則其它進程無法進入管程,被迫長時間地等待。為了解決這個問題,引入了條件變量。通常,一個進程被阻塞或掛起的條件(原因)可有多個,因此在管程中設置了多個條件變量,對這些條件變量的訪問,只能在管程中進行。

  條件變量也是一種抽象數據類型,每個條件變量保存了一個鏈表,用於記錄因該條件變量而阻塞的所有進程,同時提供的兩個操作即可表示為x.wait和x.signal,其含義為:

  ① x.wait:正在調用管程的進程因x 條件需要被阻塞或掛起,則調用x.wait 將自己插入到x 條件的等待隊列上,並釋放管程,直到x條件變化。此時其它進程可以使用該管程。

  ② x.signal:正在調用管程的進程發現x 條件發生了變化,則調用x.signal,重新啟動一個因x 條件而阻塞或掛起的進程。如果存在多個這樣的進程,則選擇其中的一個,如果沒有,則繼續執行原進程,而不產生任何結果。這與信號量機制中的signal操作不同,因為后者總是要執行s:=s+1操作,因而總會改變信號量的狀態。

  經典的進程同步問題較有代表性的是“生產者—消費者”問題、“讀者—寫者問題”、“哲學家進餐問題”等等。

  再看生產者-消費者問題

  使用記錄型信號量,利用互斥信號量mutex 實現諸進程對緩沖池的互斥使用。利用信號量empty和full分別表示緩沖池中空緩沖區和滿緩沖區的數量在每個程序中的多個wait 操作順序不能顛倒,應先執行對資源信號量的wait操作,然后再執行對互斥信號量的wait操作,否則可能引起進程死鎖。

  

   使用AND信號量。

   使用管程。首先便是為它們建立一個管程,並命名為ProclucerConsumer,或簡稱為PC。其中包括兩個過程:(1) put(item)過程。生產者利用該過程將自己生產的產品投放到緩沖池中,並用整型變量count 來表示在緩沖池中已有的產品數目,當count≥n 時,表示緩沖池已滿,生產者須等待。(2) get(item)過程。消費者利用該過程從緩沖池中取出一個產品,當count≤0 時,表示緩沖池中已無可取用的產品,消費者應等待。

   其中put(item)和get(item)如下:

  哲學家進餐問題

  有五個哲學家共用一張圓桌,分別坐在周圍的五張椅子上,在圓桌上有五個碗和五只筷子,他們的生活方式是交替地進行思考和進餐。平時,一個哲學家進行思考,飢餓時便試圖取用其左右最靠近他的筷子,只有在他拿到兩只筷子時才能進餐。進餐完畢,放下筷子繼續思考。

  使用記錄型信號量,看能會出現死鎖,如當5個哲學家同時拿起左邊的筷子時。解決辦法可以使用AND信號量,僅當哲學家的左、右兩只筷子均可用時,才允許他拿起筷子進餐。

  讀者-寫者問題

  一個數據文件或記錄,可被多個進程共享,我們把只要求讀該文件的進程稱為“Reader進程”,其他進程則稱為“Writer 進程”。允許多個進程同時讀一個共享對象,因為讀操作不會使數據文件混亂。但不允許一個Writer 進程和其他Reader 進程或Writer 進程同時訪問共享對象,因為這種訪問將會引起混亂

  使用記錄型信號量解決讀者—寫者問題為實現Reader 與Writer 進程間在讀或寫時的互斥而設置了一個互斥信號量Wmutex。另外,再設置一個整型變量Readcount 表示正在讀的進程數目。由於只要有一個Reader 進程在讀,便不允許Writer 進程去寫。因此,僅當Readcount=0,表示尚無Reader 進程在讀時,Reader 進程才需要執行Wait(Wmutex)操作。若Wait(Wmutex)操作成功,Reader 進程便可去讀,相應地,做Readcount+1 操作。同理,僅當Reader 進程在執行了Readcount 減1操作后其值為0 時,才須執行signal(Wmutex)操作,以便讓Writer進程寫。又因為Readcount是一個可被多個Reader 進程訪問的臨界資源,因此,也應該為它設置一個互斥信號量rmutex。

進程通信

  有7中常見的通信方式:

  1. 管道/匿名管道(Pipes):用於具有親緣關系的父子進程間或者兄弟進程之間的通信。

  2. 有名管道(Names Pipes): 匿名管道由於沒有名字,只能用於親緣關系的進程間通信。為了克服這個缺點,提出了有名管道。有名管道嚴格遵循先進先出(first in first out)。有名管道以磁盤文件的方式存在,可以實現本機任意兩個進程通信。

  3. 信號(Signal):信號是一種比較復雜的通信方式,用於通知接收進程某個事件已經發生;

  4. 消息隊列(Message Queuing):消息隊列是消息的鏈表,具有特定的格式,存放在內存中並由消息隊列標識符標識。管道和消息隊列的通信數據都是先進先出的原則。與管道(無名管道:只存在於內存中的文件;命名管道:存在於實際的磁盤介質或者文件系統)不同的是消息隊列存放在內核中,只有在內核重啟(即,操作系統重啟)或者顯示地刪除一個消息隊列時,該消息隊列才會被真正的刪除。消息隊列可以實現消息的隨機查詢,消息不一定要以先進先出的次序讀取,也可以按消息的類型讀取.比FIFO更有優勢。消息隊列克服了信號承載信息量少,管道只能承載無格式字節流以及緩沖區大小受限等缺。

  5. 信號量(Semaphores):信號量是一個計數器,用於多進程對共享數據的訪問,信號量的意圖在於進程間同步。這種通信方式主要用於解決與同步相關的問題並避免競爭條件。

  6. 共享內存(Shared memory):使得多個進程可以訪問同一塊內存空間,不同進程可以及時看到對方進程中對共享內存中數據的更新。這種方式需要依靠某種同步操作,如互斥鎖和信號量等。可以說這是最有用的進程間通信方式。

  7. 套接字(Sockets): 此方法主要用於在客戶端和服務器之間通過網絡進行通信。套接字是支持TCP/IP的網絡通信的基本操作單元,可以看做是不同主機之間的進程進行雙向通信的端點,簡單的說就是通信的兩方的一種約定,用套接字中的相關函數來完成通信過程。

  信號量機制是進程同步工具,交換信息量少,屬於低級通信,下面介紹幾種高級通信機制共享存儲器系統、管道通信系統以及消息傳遞系統

  共享存儲器系統中,相互通信的進程共享某些數據結構或共享存儲區,進程之間能夠通過這些空間進行通信。

  • (1) 基於共享數據結構的通信方式。在這種通信方式中,要求諸進程公用某些數據結構,借以實現諸進程間的信息交換。如在生產者—消費者問題中,就是用有界緩沖區這種數據結構來實現通信的。這種通信方式是低效的,只適於傳遞相對少量的數據。
  • (2) 基於共享存儲區的通信方式。為了傳輸大量數據,在存儲器中划出了一塊共享存儲區,諸進程可通過對共享存儲區中數據的讀或寫來實現通信。進程在通信前,先向系統申請獲得共享存儲區中的一個分區,並指定該分區的關鍵字;若系統已經給其他進程分配了這樣的分區,則將該分區的描述符返回給申請者,繼之,由申請者把獲得的共享存儲分區連接到本進程上;此后,便可像讀、寫普通存儲器一樣地讀、寫該公用存儲分區。

  管道通信。所謂“管道”,是指用於連接一個讀進程和一個寫進程以實現它們之間通信的一個共享文件,又名pipe文件。向管道(共享文件)提供輸入的發送進程(即寫進程),以字符流形式將大量的數據送入管道;而接受管道輸出的接收進程(即讀進程),則從管道中接收(讀)數據。由於發送進程和接收進程是利用管道進行通信的,故又稱為管道通信。為了協調雙方的通信,管道機制必須提供以下三方面的協調能力:互斥、同步、確認對方是否存在。

  消息傳遞系統中,進程間的數據交換是以格式化的消息(message)為單位的;在計算機網絡中,又把message稱為報文。程序員直接利用操作系統提供的一組通信命令(原語),不僅能實現大量數據的傳遞,而且還隱藏了通信的實現細節,使通信過程對用戶是透明的。分為直接通信間接通信(利用中間信箱)兩種通信方式。

  直接通信方式指發送進程利用OS所提供的發送命令,直接把消息發送給目標進程。此時,要求發送進程和接收進程都以顯式方式提供對方的標識符。

  間接通信方式指進程之間的通信需要通過作為共享數據結構的實體。該實體用來暫存發送進程發送給目標進程的消息;接收進程則從該實體中取出對方發送給自己的消息。通常把這種中間實體稱為信箱。消息在信箱中可以安全地保存,只允許核准的目標用戶隨時讀取。因此,利用信箱通信方式,既可實現實時通信,又可實現非實時通信。系統為信箱通信提供了若干條原語,分別用於信箱的創建、撤消和消息的發送、接收等。信箱分為私有信箱(用戶創建,僅用戶可收,其余只能發)、公有信箱(系統創建,都可收可發)、共享信箱(用戶創建,指定共享者,用戶和共享者均可收發)

  有兩種方式建立通信鏈路。第一種方式是由發送進程在通信之前用顯式的“建立連接”命令(原語)請求系統為之建立一條通信鏈路,在鏈路使用完后,也用顯式方式拆除鏈路,這種方式主要用於計算機網絡中。第二種方式是發送進程無須明確提出建立鏈路的請求,只須利用系統提供的發送命令(原語),系統會自動地為之建立一條鏈路,這種方式主要用於單機系統中。

  消息緩沖通信機制如下:

  

 

  發送進程在利用發送原語發送消息之前,應先在自己的內存空間設置一發送區a,把待發送的消息正文、發送進程標識符、消息長度等信息填入其中,然后調用發送原語,把消息發送給目標(接收)進程。發送原語首先根據發送區a 中所設置的消息長度a.size來申請一緩沖區i,接着把發送區a 中的信息復制到緩沖區i中。為了能將i掛在接收進程的消息隊列mq 上,應先獲得接收進程的內部標識符j,然后將i 掛在j.mq 上。由於該隊列屬於臨界資源,故在執行insert操作的前后,都要執行wait和signal操作。

  接收進程調用接收原語receive(b),從自己的消息緩沖隊列mq 中摘下第一個消息緩沖區i,並將其中的數據復制到以b 為首址的指定消息接收區內。

線程

  操作系統引入線程,是為了減少程序在並發執行時所付出的時空開銷,不僅進程之間可以並發執行,而且在一個進程中的多個線程之間亦可並發執行,使OS具有更好的並發性。通常一個進程都擁有若干個線程,至少也有一個線程,線程共享進程資源。線程作為調度和分派的基本單位,而進程作為資源擁有的基本單位

  線程的狀態參數和狀態和進程一樣,狀態參數有① 寄存器狀態,它包括程序計數器PC 和堆棧指針中的內容;② 堆棧,在堆棧中通常保存有局部變量和返回地址;③ 線程運行狀態,用於描述線程正處於何種運行狀態;④ 優先級,描述線程執行的優先程度;⑤ 線程專有存儲器,用於保存線程自己的局部變量拷貝;⑥ 信號屏蔽,即對某些信號加以屏蔽。基本狀態有就緒態,執行態,阻塞態。

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

  終止線程的方式有兩種:一種是在線程完成了自己的工作后自願退出;另一種是線程在運行中出現錯誤或由於某種原因而被其它線程強行終止。在大多數的OS中,線程被中止后並不立即釋放它所占有的資源,只有當進程中的其它線程執行了分離函數后,被終止的線程才與資源分離,此時的資源才能被其它線程利用。雖已被終止但尚未釋放資源的線程,仍可以被需要它的線程所調用,以使被終止線程重新恢復運行。為此,調用者線程須調用一條被稱為“等待線程終止”的連接命令,來與該線程進行連接。

  線程間同步和通信

  在多線程OS中通常提供多種同步機制,如互斥鎖、條件變量、計數信號量以及多讀、單寫鎖等。

  互斥鎖是一種比較簡單的、用於實現線程間對資源互斥訪問的機制。由於操作互斥鎖的時間和空間開銷都較低。互斥鎖有兩種狀態,即開鎖(unlock)和關鎖(lock)狀態。。其中的關鎖lock操作用於將mutex 關上,開鎖操作unlock則用於打開mutex。當一個線程需要讀/寫一個共享數據段時,命令首先判別mutex 的狀態,如果它已處於關鎖狀態,則試圖訪問該數據段的線程將被阻塞;而如果mutex 是處於開鎖狀態,則將mutex 關上后便去讀/寫該數據段。在線程完成對數據的讀/寫后,必須再發出開鎖命令將mutex 打開,同時還須將阻塞在該互斥鎖上的一個線程喚醒,其它的線程仍被阻塞在等待mutex打開的隊列上。

  每一個條件變量通常都與一個互斥鎖一起使用,亦即,在創建一個互斥鎖時便聯系着一個條件變量。單純的互斥鎖用於短期鎖定,主要是用來保證對臨界區的互斥進入。而條件變量則用於線程的長期等待,直至所等待的資源成為可用的資源。利用互斥鎖和條件變量來實現對資源R的訪問。線程首先對mutex執行關鎖操作,若成功便進入臨界區,然后查找用於描述該資源狀態的數據結構,以了解資源的情況。只要發現所需資源R 正處於忙碌狀態,線程便轉為等待狀態,並對mutex 執行開鎖操作后,等待該資源被釋放;若資源處於空閑狀態,表明線程可以使用該資源,於是將該資源設置為忙碌狀態,再對mutex 執行開鎖操作。下面給出了對上述資源的申請(左半部分)和釋放(右半部分)操作的描述。

 

   信號量機制有私有信號量和公有信號量。

  私用信號量屬於特定的進程所有,其數據結構存放在應用程序的地址空間中,OS並不知道私用信號量的存在,因此,一旦發生私用信號量的占用者異常結束或正常結束,但並未釋放該信號量所占有空間的情況時,系統將無法使它恢復為0(空),也不能將它傳送給下一個請求它的線程。

  公用信號量是為實現不同進程間或不同進程中各線程之間的同步而設置的,其數據結構是存放在受保護的系統存儲區中,由OS為它分配空間並進行管理,故也稱為系統信號量。如果信號量的占有者在結束時未釋放該公用信號量,則OS會自動將該信號量空間回收,並通知下一進程。

  線程的實現

  操作系統對線程的實現方式有三種:內核支持線程、用戶級線程、同時實現前兩者。

  內核支持線程中,線程的創建、撤消和切換等都是依靠內核,在內核空間實現的。系統在創建一個新進程時,便為它分配一個任務數據區PTDA(Per Task Data Area),其中包括若干個線程控制塊TCB空間。在每一個TCB中可保存線程標識符、優先級、線程運行的CPU狀態等信息。每當進程要創建一個線程時,便為新線程分配一個TCB,將有關信息填入該TCB中,並為之分配必要的資源。當PTDA中的所有TCB 空間已用完,而進程又要創建新的線程時,只要其所創建的線程數目未超過系統的允許值(通常為數十至數百個),系統可再為之分配新的TCB空間;在撤消一個線程時,也應回收該線程的所有資源和TCB。

  內核支持線程的優點:

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

  內核支持線程的缺點:對於用戶的線程切換而言,其模式切換的開銷較大,在同一個進程中,從一個線程切換到另一個線程時,需要從用戶態轉到內核態進行。

  

  用戶級線程中,線程的任務控制塊都是設置在用戶空間,而線程所執行的操作也無須內核的幫助,因而內核完全不知道用戶級線程的存在,因而使線程的切換速度特別快。對於設置了用戶級線程的系統,其調度仍是以進程為單位進行的。設置了內核支持線程的系統,則調度便是以線程為單位進行的。

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

  運行時系統實質上是用於管理和控制線程的函數(過程)的集合,其中包括用於創建和撤消線程的函數、線程同步和通信的函數以及實現線程調度的函數等。用戶級線程在切換時則不需轉入核心態,而是由運行時系統中的線程切換過程來執行切換任務。該過程將線程的CPU狀態保存在該線程的堆棧中,然后按照一定的算法選擇一個處於就緒狀態的新線程運行,將新線程堆棧中的CPU狀態裝入到CPU相應的寄存器中,一旦將棧指針和程序計數器切換后,便開始了新線程的運行。當線程需要系統資源時,是將該要求傳送給運行時系統,由運行時系統通過相應的系統調用來獲得系統資源的

  內核控制線程又稱為輕型進程LWP,每一個進程都可擁有多個LWP,同用戶級線程一樣,每個LWP都有自己的數據結構(如TCB),它們也可以共享進程所擁有的資源。LWP 可通系統調用來獲得內核提供的服務,這樣,當一個用戶級線程運行時,只要將它連接到一個LWP上,此時它便具有了內核支持線程的所有屬性。在一個系統中的用戶級線程數量可能很大,為了節省系統開銷,不可能設置太多的LWP,而把這些LWP 做成一個緩沖池,稱為“線程池”。用戶進程中的任一用戶線程都可以連接到LWP池中的任何一個LWP上。為使每一用戶級線程都能利用LWP與內核通信,可以使多個用戶級線程多路復用一個LWP,但只有當前連接到LWP上的線程才能與內核通信,其余進程或者阻塞,或者等待LWP。而每一個LWP都要連接到一個內核級線程上,這樣,通過LWP可把用戶級線程與內核線程連接起來,用戶級線程可通過LWP來訪問內核,但內核所看到的總是多個LWP 而看不到用戶級線程。亦即,由LWP 實現了在內核與用戶級線程之間的隔離,從而使用戶級線程與內核無關。

 

  用戶級線程的優點:

  • 線程切換不需要轉換到內核空間,模式切換開銷小;
  • 調度算法可以是進程專用的;
  • 用戶級線程的實現與操作系統平台無關,因為對於線程管理的代碼是在用戶程序內的,屬於用戶程序的一部分,所有的應用程序都可以對之進行共享。因此,用戶級線程甚至可以在不支持線程機制的操作系統平台上實現。

  用戶級線程的缺點:

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

 

  組合方式有些操作系統把用戶級線程和內核支持線程兩種方式進行組合,在組合方式線程系統中,內核支持線程的建立、調度和管理有內核完成,同時,也允許用戶應用程序建立、調度和管理用戶級線程。

參考

  《計算機操作系統》 第三版 湯小丹

  Jason_liu進程的阻塞和掛起的區別

 


免責聲明!

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



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