1. 程式人生 > >操作系統中線程的實現模型

操作系統中線程的實現模型

線程 都是 全局 發生 線程數 相關 wid 2個 機制

轉自https://blog.csdn.net/fuzhongmin05/article/details/55802984

1、為什麽需要線程?

首先需要回答一個問題,為什麽操作系統需要線程。如果非要說是為什麽需要線程,還不如說為什麽需要進程中還有其它進程。這些進程中包含的其它迷你進程就是線程。進程有以下缺陷:

1、進程只能在一個時間內幹一件事(執行一個程序執行流),而如果想同時幹兩件或多件事情,進程就不夠用了。
2、進程在執行過程中如果阻塞,例如等待輸入,整個進程就將掛起,而無法繼續執行。這樣,即使進程裏面有部分工作不依賴於輸入數據,也無法推進。
為了解決這個問題,人們發明了線程。線程之所以說是迷你進程,是因為線程和進程有很多相似之處,比如線程和進程的狀態都有運行,就緒,阻塞狀態。這幾種狀態理解起來非常簡單,當進程所需的資源沒有到位時會是阻塞狀態,當進程所需的資源到位但CPU沒有到位時是就緒狀態,當進程既有所需的資源,又有CPU時,就為運行狀態。使用多線程,每個線程僅僅需要處理自己那一部分應該完成的任務,而不用去關心和其它線程的沖突。因此簡化了編程模型。
更具體的說,線程的好處如下:

1、在很多程序中,需要多個線程互相同步或互斥的並行完成工作,而將這些工作分解到不同的線程中去無疑簡化了編程模型。
2、因為線程相比進程來說,更加的輕量,故線程的創建和銷毀的代價變得更小。
2、線程提高了性能,雖然線程宏觀上是並行的,但微觀上卻是串行(單CPU或單核時)。從CPU角度線程並無法提升性能,但如果某些線程涉及到等待資源(比如IO,等待輸入)時,多線程允許進程中的其它線程繼續執行而不是整個進程被阻塞,因此提高了CPU的利用率,從這個角度會提升性能。
3、在多CPU或多核的情況下,使用線程不僅僅在宏觀上並行,在微觀上也是並行的。
進程有自己獨立的內存地址空間,而線程共享進程的內存地址空間。

2、經典的線程模型

另一個看進程和線程的角度是進程模型基於兩類不同的概念:資源的組織和執行。在過去沒有線程的操作系統中,資源的組織和執行都是由進程完成的。但這兩者很多時候需要加以區分,這也是為什麽需要引入線程。進程是用於組織資源的單位,進程將相關的資源組織在一起,這些資源包括:內存地址空間,程序,數據等,將這些以進程的形式組織起來可以使得操作系統管理這些資源更為容易。而線程,是每一個進程中執行的一個條線。線程雖然共享進程中的大多數資源,但線程也需要自己的一些資源,比如:用於標識下一條執行指令的程序計數器,一些容納局部變量的寄存器,以及用於表示執行的歷史的棧。

總而言之:進程是組織資源的最小單位,而線程是安排CPU執行的最小單位。其實在一個進程中多個線程並行和在操作系統中多個進程並行非常類似,只是線程共享的是地址空間,而進程共享的是物理內存,打印機,鍵盤等資源。每一個進程和線程所獨自占有的資源如表1所示。

表1

進程占有的資源 線程占有的資源
地址空間
全局變量 寄存器
打開的文件 程序計數器
信號量 狀態
賬戶信息


其中,線程可以共享進程獨占的資源。我們常用的術語“多線程”一般指的是在同一個進程中多個線程的並發執行。線程是進程裏面的一個執行上下文,或者執行序列。同一個地址空間裏面的所有線程就構成了進程。線程是CPU切換的最小單位,進程是資源分配的最小單位。

例如:當我們使用Word時,實際上是打開了多個線程。這些線程一個負責顯示,一個接受輸入,一個定時存盤。這些線程一起運轉(同時參與競爭CPU),讓我們感覺到輸入和屏幕顯示同時發生,而不用鍵入一些字符,等待一會兒才看到屏幕顯示。

3、線程管理與實現方式

要管理線程就要維持線程的各種信息,存放這些信息的數據結構稱為線程控制塊。線程共享一個進程空間,因此許多資源是共享的(如地址空間、全局變量、文件等),這些共享資源存放在進程控制塊即可。還有一些不被共享的資源和信息(如程序計數器、寄存器等),需要存放在線程控制塊裏。 由誰來管理線程有兩種選擇:一是讓進程自己來管理線程;二是讓操作系統來管理線程。由進程自己管理就是用戶態線程實現,由操作系統管理就是內核態線程實現。

3.1 線程實現在用戶空間下

當線程在用戶空間下實現時,操作系統對線程的存在一無所知,操作系統只能看到進程,而不能看到線程。所有的線程都是在用戶空間下實現。在操作系統看來,每一個進程只有一個線程。過去的操作系統大部分是這種實現方式,這種方式的好處之一就是即使操作系統不支持線程,也可以通過庫函數來支持線程。在這種模式下,每一個進程中都維護著一個線程表來追蹤本進程中的線程,這個表中包含表1中每個線程獨占的資源,比如棧,寄存器,狀態等,如圖1所示。

技術分享圖片 圖1 線程實現在用戶空間下

這種模式當一個線程完成了其工作或等待需要被阻塞時,其調用系統過程阻塞自身,然後將CPU交由其它線程。用戶線程多見於一些歷史悠久的操作系統,如UNIX操作系統。這種的模式的好處,首先,是在用戶空間下進行進程切換的速度要遠快於在操作系統內核中實現。其次,在用戶空間下實現線程使得程序員可以實現自己的線程調度算法。比如進程可以實現垃圾回收器來回收線程。還有,當線程數量過多時,由於在用戶空間維護線程表,不會占用大量的操作系統空間。最後是靈活性,由於操作系統不需要知道線程的存在,所以在任何操作系統上都能應用。

這種模式最致命的缺點也是由於操作系統不知道線程的存在,因此當一個進程中的某一個線程進行系統調用時,比如缺頁中斷而導致線程阻塞,此時操作系統會阻塞整個進程,即使這個進程中其它線程還在工作。此外會造成編程困難,我們在寫程序的時候必須仔細斟酌在什麽時候應該讓出CPU給別的線程使用。還有一個問題是假如進程中一個線程長時間不釋放CPU,因為用戶空間並沒有時鐘中斷機制,會導致此進程中的其它線程得不到CPU而持續等待。

3.2 線程實現在操作系統內核中

在這種模式下,操作系統知道線程的存在。此時線程表存在操作系統內核中,如圖2所示。線程應該是CPU調度的基本單位,操作系統要管理線程,就要保持維護線程的各種資料,即將線程控制塊存放在操作系統內核空間。這樣操作系統內核就同時保有進程控制塊和線程控制塊。

技術分享圖片 圖2 線程實現在操作系統內核空間中


在這種模式下,所有可能阻塞線程的調用都以系統調用(System Call)的方式實現,相比在用戶空間下實現線程造成阻塞的運行時調用(System runtime call)成本會高出很多。當一個線程阻塞時,操作系統可以選擇將CPU交給同一進程中的其它線程,或是其它進程中的線程,而在用戶空間下實現線程時,調度只能在本進程中執行,直到操作系統剝奪了當前進程的CPU。

因為在內核模式下實現進程的成本更高,一個比較好的做法是將線程回收利用,當一個線程需要被銷毀時,僅僅是修改標記位,而不是直接銷毀其內容,當一個新的線程需要被創建時,也同樣修改被“銷毀”的線程其標記位即可。名為內核態線程實現的優點是用戶編程簡單,用戶程序員在編程的時候無需關心線程的調度,即無需擔心線程什麽時候會執行,什麽時候會掛起。如果一個線程執行阻塞操作,操作系統可以從容地調度另外一個線程執行。因為操作系統能監控所有線程。

這種模式下同樣還是有一些弊端,例如效率較低。因為線程在內核態實現,每次線程切換都需要陷入到內核,由操作系統來進行調度。其次,占用內核稀缺的內存資源。操作系統需要維護線程表,操作系統所占的內存空間一旦裝載結束後就已經固定,無法動態改變。由於線程的數量大大高於進程的數量,那麽隨著線程數量的增加,操作系統內核空間將迅速耗盡。

3.3 現代操作系統的線程實現模型—混合模式

鑒於用戶態和內核態都存在缺陷,現代操作系統使用的是將二者結合起來。用戶態的執行系統負責進程內部線程在非阻塞時的切換;內核態的操作系統負責阻塞線程的切換,即我們同時實現內核態和用戶態線程管理。其中內核態線程數量較少,而用戶態線程數量多。每個內核態線程可以服務一個或多個用戶態線程。換句話說,用戶態線程被多路復用到內核態線程上。混合模式下,用戶空間中的進程管理自己的線程,操作系統內核中有一部分內核級別的線程,如圖3所示。

例如,某個進程有5個線程,我們可以將5個線程分成兩組,一組3個線程,另一組2個線程。每一組線程使用一個內核線程。這樣,該進程將使用兩個內核線程。如果一個線程阻塞,則與其同屬於一組的線程皆阻塞,但另外一組線程卻可以繼續執行。在這種模式下,在分配線程時,我們可將需要執行阻塞操作的線程設為內核態線程,而不會執行阻塞操作的線程設為用戶態線程。這樣我們就可以獲得兩種態勢實現下的優點,而避免其缺點。

技術分享圖片 圖3 線程實現的混合模型

在這種模式下,操作系統只能看到內核線程。用戶空間線程基於操作系統線程運行。因此,程序員可以決定使用多少用戶空間線程以及操作系統線程,這無疑具有更大的靈活性。而用戶空間線程的調度和前面所說的在用戶空間下執行實現線程是一樣的,同樣可以自定義實現。

操作系統中線程的實現模型