操作系統 進程(下)
一、進程同步
什麽是同步?同步就是說一個任務要等另一個執行完畢才能繼續執行,而不是同時執行。我們都知道,進程有異步性,這種性質會導致操作系統的混亂。進程同步,指的是進程之間的執行次序的管理,就是為了解決進程異步性的這種混亂。
(1)直接制約和間接制約。
進程之間有兩種制約關系。分別是直接制約和間接制約。直接制約指的是進程間的合作,即一個進程需要另一個進程的配合,否則會阻塞。如輸出緩沖區為空的時候,輸出進程就會阻塞,輸出進程依賴輸入進程不斷的輸入。間接制約指的是對於某種資源,同時只能有一個進程占用,你用的時候,別人就不能用。
(2)同步機制應遵循的規則。
這是所有的同步機制所需要遵循的規則:
1)空閑讓進。資源空閑的時候,允許進程訪問。
2)忙則等待。資源被占用的時候,進程必須等待。
3)有限等待。應保證進程有限時間能訪問到資源,不能無限等待。
4)讓權等待。運行中的進程不能訪問指定資源時,應釋放處理機。
二、PV操作
PV操作的鼎鼎大名,想必很多人都聽說過。它就是經典的實現最基本的進程同步機制的一對操作。為什麽叫PV操作呢?它是鼎鼎大名的計算機學家狄克斯特拉用荷蘭語定義的,在荷蘭文中,通過叫passeren,釋放叫vrijgeven。P操作又叫Wait(S),本質是使用資源,V操作又叫Sign(S),本質是釋放資源。PV操作都是原子操作,要麽全做,要麽全不做,並且PV操作是成對的。我們來詳細看看PV操作的原理,是怎麽實現進程同步的。PV操作跟信號量是分不開的,先看看什麽是信號量。
(1)什麽是信號量?
信號量是一種數據結構。包括整形信號量、記錄型信號量、AND型信號量和信號量集等。不同的信號量對應不同的數據結構,也對應不同的PV操作。信號量和操作它的PV操作構成了對
應的信號量機制,用來控制進程同步。
(2)整型信號量。
顧名思義,整形信號量的數據結構就是一個簡單的整形,一般用整形S來表示。其上的PV操作如下:
(這裏的程序代碼都是Pascal代碼)
wait(S): while S <= 0 do no-op; s := s - 1 sinal(S): s := s + 1
如上,S代表的是資源數目。對於wait(S)操作,當資源數目小於等於0的時候,就一直等待。若有資源,就跳出循環,使用一個資源。
對於sinal(S)操作,每次執行都釋放一個資源。
(3)記錄型信號量。
這個信號量比整形信號量增加了一個標識進程指針的屬性,指向所有等待的進程鏈表。
PV操作與整形信號量的區別在於,wait()時,若s<=0,那麽阻塞自身,放棄處理機。signal()後,判斷若s<=0,就喚醒一個進程。它的好處是當進程請求不到資源的時候,不會無限等待。
(4)AND型信號量。
AND型信號量是針對多個臨界資源而言的。即將進程運行中所需要的所有資源一次性分配給進程,進程運行完畢後釋放所有資源。相當於把進程所需的所有資源捆綁在一起了。做法就是在wait操作中增加一個AND條件:只有當進程所需的所有資源處於空閑狀態時,進程才能繼續操作。
(5)信號量集。
信號量集指的是一次性對N個某類資源的請求處理。上面的AND型信號量指的是對多個不同類型資源的處理,而信號量集指的是對同類的多個資源的處理,也相當於AND型信號量的特殊情況。
(6)管程
由於進程對某一個資源進行操作的時候,都需要自帶一對PV操作,為了避免這種情況,把某個資源和進程對其進行的操作包裝起來,這樣的一個模塊稱為管程。它是操作系統中的一個資源管理模塊,供進程調用。可以看到,管程實現了面向對象的思想。
(7)條件變量
在上面的進程同步的實現中都有一個很嚴重的隱含問題,那就是,如果某個進程一直不釋放某個資源,其他進程就只能無止休地等待。條件變量的意義在於,除了原本的資源空閑就讓進、處理完就釋放這樣的邏輯外,還有其他的條件。例如:資源空閑且XX條件,就讓進。處理完成或XX條件就釋放資源。這些額外的條件,就叫條件變量。
三、進程通信
上面的通過信號量進行的進程同步,其本質是一種低級的通信機制。進程之間無法大量交換信息。那麽兩個進程之間想要實現大量的、頻繁的信息交換,該怎麽做呢?這就是高級通信機制了。高級通信機制有三大類:
(1)共享存儲器系統。
存儲器即內存,共享存儲器,顧名思義,就是通信的兩個進程通過共享的一塊內存區域來通信,一個負責讀一個負責寫。而實際上面的信號量也是一種共享存儲器系統,只不過進程間共享的是一個數據結構,並用PV操作對數據結構進行操作。
(2)消息傳遞系統。
進程間通過指定格式的消息進行通信。消息格式通常就是一個包含地址的頭和一個包含內容的body。這種格式也叫做協議。我們常見的網絡協議也是這種方式。消息傳遞系統分為直接通信方式和間接通信方式。直接通信方式即通信的進程雙方都知道對方的存在,並在消息頭中攜帶了自身和對方的地址信息。間接通信方式即進程間的消息傳遞不是直接傳遞給對方,而是有一個中間實體暫存、並轉發,這樣避免了進程雙方接收、發送數據的速率不統一導致的進程阻塞。
(3)管道通信。
管道是一種連接讀進程和寫進程的共享文件——pipe文件,其本質是固定大小的緩沖區,這個緩沖區將2個進程連接起來,這兩個進程對管道是互斥的訪問,且寫進程寫入數據後便阻塞,直到讀進程取走所有數據才被喚醒繼續寫數據。這種一次性的讀操作和寫操作,雖然會導致進程堵塞,但是在讀寫的過程中無須維護讀寫指針,效率非常高。
四、線程
線程是什麽?線程就為了使操作系統能夠有更好的並發而創建的,相當於只擁有少量資源的進程——輕型進程。在這種多線程操作系統中,進程是擁有系統資源的基本單位,包含多個線程,為其提供資源,而進程本身不再作為可執行的實體,當進程執行的時候,實際上是其中的某個線程在執行。
(1)線程執行的本質。
理解線程就必須深入理解並發。並發的本質就是單處理機系統永遠都是線性執行任務的。而線程的本質就是將原本為實現進程的時間片劃分的更細,假設在某個單處理機操作系統中,時間片為20毫秒,即一個進程的單次執行時間為20毫秒,在這個進程內有50個線程在執行,那麽劃分後,平均每個線程的執行時間肯定小於0.4毫秒。不過對於大部分任務而言,這仍舊是足夠的。線程無非就是這樣。
(2)線程的類型和實現。
1)用戶級線程(User Level Thread)——ULT。
這種線程的實現非常簡單,對於處理器而言,它仍舊是在進行進程切換,並不知道有線程的存在。如果每個進程相當於一個車子,那每個線程都相當於一個司機,線程切換就是不斷在換司機。
那麽在進程內部如何分割出各種多線程的呢?進程中有一個函數集合,專門用來管理和控制線程的執行。這個函數集合被稱為運行時系統。進程執行時就是執行它的運行時系統,對其中的線程進行切換管理。線程的運行時信息——線程控制塊TCB存放在各自的堆棧中,每次切換的時候,運行時系統就從線程的堆棧中取得對應的運行時信息,設置CPU的寄存器中,之後便可以運行。值得註意的是,線程是不能直接調用系統資源的,線程需要系統資源時,需要由運行時系統來調用分配。
2)內核支持線程(KernelSupported Thread)——KST。
這種線程的創建、撤銷、切換就不是依賴進程,是直接像進程調度一樣由內核控制,由於線程基本不用有資源,所以這種調度也很快。內核支持線程的線程優先級通常比用戶級線程要高很多。
那麽內核支持線程如何實現的呢?創建一個進程時,系統為之分配一個任務數據區(Per Task Data Area),其中包含若幹線程控制塊TCB,這些TCB並非存放在進程資源內存中,而是保存在CPU的寄存器中。然後進行跟PCB非常類似的由處理器切換控制。
3)組合方式,由輕型進程(Light weight process)——LWP實現。
內核支持多KST線程的創建,同時支持ULT線程的創建,這種支持是通過輕型進程LWP實現的。輕型進程LWP的本質就是一個KST進程,它的特點就是能夠讓ULT連接,當ULT連接它的時候,就相當於在調用KST,可以實現KST的所有功能。所以一般LWP都是用線程池來實現的。可以看到LWP的目的就是為了讓用戶級線程ULT直接能調用系統資源。
參考:《計算機操作系統(湯子瀛)》
操作系統 進程(下)