第七章-----執行緒

飄過的小熊發表於2016-09-12

標籤(空格分隔): 作業系統之哲學原理



程式的分身術——執行緒

執行緒就是我們為了讓一個程式能夠同時幹多件事情而發明的分身術

線上程模式下,一個程式至少有一個執行緒,但也可以有多個執行緒。將程式分解為執行緒還可以有效利用多處理器和多核計算機。在沒有執行緒的情況下,增加一個處理器並不能提高一個程式的執行速度,但是分解為多個執行緒,可以讓不同的執行緒執行在不同的處理器上,從而提高程式的執行速度。


執行緒管理

執行緒管理與程式管理類似:要維持執行緒的各種資訊。存放這些資訊的資料結構稱為執行緒控制表或者執行緒控制塊

執行緒共享一個程式空間,許多資源是共享的,共享的資源是放在程式控制塊。但是執行緒是不同的執行序列,總有不能共享的資源。而這些不能共享的資源就是放在執行緒控制塊

如何才能確定到底哪些資源是同一程式的不同執行緒共享?哪些是不共享的呢?

規律就是:應當讓共享的資源越多越好。因為我們發明執行緒的目的就是為了協作,共享是我們不懈的追求。

資源是否應該設定為共享的評判標準

如果某資源不獨享會導致執行錯誤,則該資源就應該獨享,否則就應該共享

執行緒共享的資源:

  • 地址空間
  • 全域性變數
  • 檔案
  • 子程式
  • 鬧鈴
  • 訊號及訊號服務程式
  • 記帳資訊

執行緒獨享資源:

  • 程式計數器
  • 暫存器
  • 狀態字

執行緒模型的實現(實際上也是執行緒排程的實現)

執行緒是在程式基礎上的二次併發
與程式一樣,執行緒本身對應某種物理實現,也需要儲存和排程。器儲存是直接附於程式儲存方案上。

執行緒排程的兩種方式:

  • 可以由程式負責:使用者態實現
  • 可以由作業系統負責:核心態實現

使用者態和核心態的判斷:以執行緒表所處的位置為依據,位於核心的叫核心態實現,位於使用者層的叫使用者態實現

注意到,我們在研究程式的時候沒有討論程式的實現方式(使用者態實現還是核心態實現),畢竟程式在CPU實現併發,而CPU是由作業系統直接管理的。因此只存在核心態

核心態執行緒實現

作業系統如何管理執行緒?

與程式類似,要管理執行緒就要持有執行緒的所有資訊,將執行緒控制塊存放在作業系統的核心空間,這樣作業系統就同時持有程式控制塊和執行緒控制塊。

作業系統管理執行緒(核心態執行緒)實現有什麼好處?

  • 最重要的好處是使用者程式設計簡單
  • 如果一個執行緒執行了阻塞操作,作業系統可以從容的排程另一個執行緒執行。作業系統可以監控所有的執行緒

核心態執行緒實現的缺點?

  • 效率較低,執行緒在核心態實現,每次執行緒切換都要進入核心,花銷比較大。
  • 核心態實現佔用核心稀缺資源,因為作業系統需要維護執行緒表,本來之前作業系統的記憶體空間一旦家在結束後就是固定的了,隨著執行緒數量的增加,核心空間將被迅速耗盡。如果需要建立程式但是核心空間已經滿了怎麼辦?“死掉”,投降才是真本事。
  • 最致命的是:核心態的實現需要修改作業系統。要向人家證明執行緒管理是有用的,於是先實現使用者態實現。

使用者態執行緒實現

使用者態實現意味著什麼?使用者態實現是什麼意思?

就是使用者自己做執行緒的切換,自己管理執行緒的資訊,而作業系統無需知道執行緒的存在

使用者態是如何實現執行緒排程的尼?

就是使用者自己寫一個執行系統作為排程器,也就是說除了正常執行任務的執行緒外,還有一個專門負責執行緒排程的執行緒。大家都在使用者態下執行,都是小弟,誰也不比誰更佔優勢,要想取得CPU的控制權只能靠大家的合作。

使用者態實現執行緒的優點

  • 靈活性很強,作業系統無需知道執行緒的存在,在任何作業系統上都能應用
  • 執行緒切換很快
  • 不用修改作業系統,實現容易

使用者態實現的缺點

  • 程式設計變得很詭異,我們必須仔細斟酌什麼時候讓出CPU給別的執行緒使用,而讓出的時機對執行緒的效率和可靠性有很大的影響
  • 使用者態執行緒實現無法完全達到終極目的:程式級多道程式設計
    • 因為線上程的執行過程中,如果一個執行緒受阻,將無法把控制權交出來(因為受阻後無法執行交出CPU的命令了),這樣的話,整個程式都無法進行。作業系統馬上過來收回控制權並交給其他程式使用。於是一個執行緒受阻導致整個程式受阻,執行緒對程式的分身計劃失敗。

想辦法挽救使用者態實現

  • 不讓執行緒阻塞

    • 將所有的操作改成非阻塞操作,比如讀寫磁碟,收發資料包,,。,但是不可行呀,因為修改系統呼叫就要修改作業系統,這是其一。第二是沒有作業系統的人員幫忙。最後一點是,很多系統呼叫的語義裡面就包括阻塞,說明部分的阻塞式其正確執行的前提。綜上,改為非阻塞操作的做法不可取
    • 不讓執行緒呼叫阻塞操作。只需要線上程進行任何系統呼叫之前確認一下該呼叫是否會發生阻塞。寫一個包裹將系統呼叫包裹起來,這個包裹專門用於檢查。
  • 程式阻塞後想辦法啟用受阻程式的其他執行緒

這種方式非常依賴於作業系統,因為程式阻塞後CPU控制權就交到了作業系統的手中,但是想辦法讓作業系統在程式切換的時候先不去切換程式,而是通知受阻程式的執行系統,詢問是不是還有其他可以執行的執行緒,要是還有的話,就把控制權交過去,從而排程另一個執行緒執行。這就是傳說中的排程器啟用

但是這種做法也有兩個缺點:

  • 要修改作業系統,但是修改的範圍比較小,還是可以忍受
  • 這種作業系統呼叫使用者態執行系統的做法違反了我們的層次架構原則,

作業系統的執行緒實現模型

因為使用者態與核心態都有缺陷,於是就中庸之道。
使用者態的執行系統負責內部執行緒在非阻塞時的切換,核心態的作業系統負責阻塞執行緒的切換,核心態執行緒數量較少,使用者態執行緒數量較多。使用者態執行緒可以多路複用到核心態執行緒上


多執行緒的關係

退出多執行緒的目的就是為了實現程式級併發。執行緒在共享地址空間的時候也會產生矛盾:

  • 執行緒之間如何通訊?
  • 執行緒之間如何同步?

討論:從使用者態進入核心態

什麼情況會造成一個執行緒從使用者態進入到核心態?

  • 如果程式在·執行過程中發生中斷或者異常,系統將自動切換到核心態來執行或異常處理機制
  • 程式進行系統呼叫也將造成從使用者態進入到核心態的轉換

討論:執行緒的困惑—–確定性與非確定性

系統執行可以說是確定性和非確定性的,確定是說的每個程式的執行結果基本確定,不確定是指每個程式的執行順序不確定,而每個單執行緒執行效率和執行正確性均存在不確定性。

從某種程度上來說,執行緒與流水線分別是軟體層和硬體層不確定性的根源。但其帶來的作業系統,編譯系統和指令集系統結構的高度複雜是否值得,也許不容易被回答!!


國際慣例:總結

執行緒是程式級併發。圍繞執行緒有很多的問題值得我們關心。

相關文章