內核線程、輕量級進程、用戶線程

轉載:http://www.cnitblog.com/tarius.wu/articles/2277.html

轉載:http://www.fansoo.com/blog/2011/kernel-threads-lightweight-processes-threads-and-linuxthreads-library-users/

內核線程

內核線程只運行在內核態,不受用戶態上下文的拖累。

  • 處理器競爭:可以在全系統範圍內競爭處理器資源;
  • 使用資源:唯一使用的資源是內核棧和上下文切換時保持寄存器的空間
  • 調度:調度的開銷可能和進程自身差不多昂貴
  • 同步效率:資源的同步和數據共享比整個進程的數據同步和共享要低一些。

輕量級進程

輕量級進程(LWP)是建立在內核之上並由內核支持的用戶線程,它是內核線程的高度抽象,每一個輕量級進程都與一個特定的內核線程關聯。內核線程只能由內核管理並像普通進程一樣被調度。

輕量級進程由clone()系統調用創建,參數是CLONE_VM,即與父進程是共享進程地址空間和系統資源。

與普通進程區別:LWP只有一個最小的執行上下文和調度程序所需的統計信息。

  • 處理器競爭:因與特定內核線程關聯,因此可以在全系統範圍內競爭處理器資源
  • 使用資源:與父進程共享進程地址空間
  • 調度:像普通進程一樣調度

用戶線程

用戶線程是完全建立在用戶空間的線程庫,用戶線程的創建、調度、同步和銷燬全又庫函數在用戶空間完成,不需要內核的幫助。因此這種線程是極其低消耗和高效的。

  • 處理器競爭:單純的用戶線程是建立在用戶空間,其對內核是透明的,因此其所屬進程單獨參與處理器的競爭,而進程的所有線程參與競爭該進程的資源。
  • 使用資源:與所屬進程共享進程地址空間和系統資源。
  • 調度:由在用戶空間實現的線程庫,在所屬進程內進行調度

Linux使用的線程庫

LinuxThreads是用戶空間的線程庫,所採用的是線程-進程1對1模型(即一個用戶線程對應一個輕量級進程,而一個輕量級進程對應一個特定的內核線程),將線程的調度等同於進程的調度,調度交由內核完成,而線程的創建、同步、銷燬由核外線程庫完成(LinuxThtreads已綁定到 GLIBC中發行)。

在LinuxThreads中,由專門的一個管理線程處理所有的線程管理工作。當進程第一次調用pthread_create()創建線程時就會先創建(clone())並啓動管理線程。後續進程pthread_create()創建線程時,都是管理線程作爲pthread_create()的調用者的子線程,通過調用clone()來創建用戶線程,並記錄輕量級進程號和線程id的映射關係,因此,用戶線程其實是管理線程的子線程。

LinuxThreads只支持調度範圍爲PTHREAD_SCOPE_SYSTEM的調度,默認的調度策略是SCHED_OTHER。

用戶線程調度策略也可修改成SCHED_FIFO或SCHED_RR方式,這兩種方式支持優先級爲0-99,而SCHED_OTHER只支持0。

  • SCHED_OTHER 分時調度策略,
  • SCHED_FIFO   實時調度策略,先到先服務
  • SCHED_RR     實時調度策略,時間片輪轉

SCHED_OTHER是普通進程的,後兩個是實時進程的(一般的進程都是普通進程,系統中出現實時進程的機會很少)。SCHED_FIFO、 SCHED_RR優先級高於所有SCHED_OTHER的進程,所以只要他們能夠運行,在他們運行完之前,所有SCHED_OTHER的進程的都沒有得到執行的機會。

附: 基礎知識(線程和進程)

按照教科書上的定義,進程是資源管理的最小單位,線程是程序執行的最小單位。在操作系統設計上,從進程演化出線程,最主要的目的就是更好的支持SMP以及減小(進程/線程)上下文切換開銷。

無論按照怎樣的分法,一個進程至少需要一個線程作爲它的指令執行體,進程管理着資源(比如cpu、內存、文件等等),而將線程分配到某個cpu上執行。一個進程當然可以擁有多個線程,此時,如果進程運行在SMP機器上,它就可以同時使用多個cpu來執行各個線程,達到最大程度的並行,以提高效率;同時,即使是在單cpu的機器上,採用多線程模型來設計程序,正如當年採用多進程模型代替單進程模型一樣,使設計更簡潔、功能更完備,程序的執行效率也更高,例如採用多個線程響應多個輸入,而此時多線程模型所實現的功能實際上也可以用多進程模型來實現,而與後者相比,線程的上下文切換開銷就比進程要小多了,從語義上來說,同時響應多個輸入這樣的功能,實際上就是共享了除cpu以外的所有資源的。

針對線程模型的兩大意義,分別開發出了核心級線程和用戶級線程兩種線程模型,分類的標準主要是線程的調度者在覈內還是在覈外。前者更利於併發使用多處理器的資源,而後者則更多考慮的是上下文切換開銷。在目前的商用系統中,通常都將兩者結合起來使用,既提供核心線程以滿足smp系統的需要,也支持用線程庫的方式在用戶態實現另一套線程機制,此時一個核心線程同時成爲多個用戶態線程的調度者。正如很多技術一樣,”混合”通常都能帶來更高的效率,但同時也帶來更大的實現難度,出於”簡單”的設計思路,Linux從一開始就沒有實現混合模型的計劃,但它在實現上採用了另一種思路的”混合”。

在線程機制的具體實現上,可以在操作系統內核上實現線程,也可以在覈外實現,後者顯然要求核內至少實現了進程,而前者則一般要求在覈內同時也支持進程。核心級線程模型顯然要求前者的支持,而用戶級線程模型則不一定基於後者實現。這種差異,正如前所述,是兩種分類方式的標準不同帶來的。

當核內既支持進程也支持線程時,就可以實現線程-進程的”多對多”模型,即一個進程的某個線程由核內調度,而同時它也可以作爲用戶級線程池的調度者,選擇合適的用戶級線程在其空間中運行。這就是前面提到的”混合”線程模型,既可滿足多處理機系統的需要,也可以最大限度的減小調度開銷。絕大多數商業操作系統(如Digital Unix、Solaris、Irix)都採用的這種能夠完全實現POSIX1003.1c標準的線程模型。在覈外實現的線程又可以分爲”一對一”、”多對一”兩種模型,前者用一個核心進程(也許是輕量進程)對應一個線程,將線程調度等同於進程調度,交給核心完成,而後者則完全在覈外實現多線程,調度也在用戶態完成。後者就是前面提到的單純的用戶級線程模型的實現方式,顯然,這種核外的線程調度器實際上只需要完成線程運行棧的切換,調度開銷非常小,但同時因爲核心信號(無論是同步的還是異步的)都是以進程爲單位的,因而無法定位到線程,所以這種實現方式不能用於多處理器系統,而這個需求正變得越來越大,因此,在現實中,純用戶級線程的實現,除算法研究目的以外,幾乎已經消失了。

Linux內核只提供了輕量進程的支持,限制了更高效的線程模型的實現,但Linux着重優化了進程的調度開銷,一定程度上也彌補了這一缺陷。目前最流行的線程機制LinuxThreads所採用的就是線程-進程”一對一”模型,調度交給核心,而在用戶級實現一個包括信號處理在內的線程管理機制。 Linux-LinuxThreads的運行機制正是本文的描述重點。

Reference:

在現代操作系統中,進程支持多線程。進程是資源管理的最小單元;而線程是程序執行的最小單元。一個進程的組成實體可以分爲兩大部分:線程集合資源集。進程中的線程是動態的對象;代表了進程指令的執行。資源,包括地址空間、打開的文件、用戶信息等等,由進程內的線程共享。

線程有自己的私有數據:程序計數器,棧空間以及寄存器。

Why Thread?(傳統單線程進程的缺點)

<!--[if !supportLists]-->1. <!--[endif]-->現實中有很多需要併發處理的任務,如數據庫的服務器端、網絡服務器、大容量計算等。

<!--[if !supportLists]-->2. <!--[endif]-->傳統的UNIX進程是單線程的,單線程意味着程序必須是順序執行,不能併發;既在一個時刻只能運行在一個處理器上,因此不能充分利用多處理器框架的計算機。

<!--[if !supportLists]-->3. <!--[endif]-->如果採用多進程的方法,則有如下問題:
a. fork 一個子進程的消耗是很大的,fork是一個昂貴的系統調用,即使使用現代的寫時複製(copy-on-write)技術。
b. 各個進程擁有自己獨立的地址空間,進程間的協作需要複雜的IPC技術,如消息傳遞和共享內存等。

多線程的優缺點

多線程的優點和缺點實際上是對立統一的。

支持多線程的程序(進程)可以取得真正的並行(parallelism),且由於共享進程的代碼和全局數據,故線程間的通信是方便的。它的缺點也是由於線程共享進程的地址空間,因此可能會導致競爭,因此對某一塊有多個線程要訪問的數據需要一些同步技術。

三種線程——內核線程、輕量級進程、用戶線程

內核線程

內核線程就是內核的分身,一個分身可以處理一件特定事情。這在處理異步事件如異步IO時特別有用。內核線程的使用是廉價的,唯一使用的資源就是內核棧和上下文切換時保存寄存器的空間。支持多線程的內核叫做多線程內核(Multi-Threads kernel )。

輕量級進程[*]

輕量級線程(LWP)是一種由內核支持的用戶線程。它是基於內核線程的高級抽象,因此只有先支持內核線程,纔能有LWP。每一個進程有一個或多個LWPs,每個LWP由一個內核線程支持。這種模型實際上就是恐龍書上所提到的一對一線程模型。在這種實現的操作系統中,LWP就是用戶線程。

由於每個LWP都與一個特定的內核線程關聯,因此每個LWP都是一個獨立的線程調度單元。即使有一個LWP在系統調用中阻塞,也不會影響整個進程的執行。

輕量級進程具有侷限性。首先,大多數LWP的操作,如建立、析構以及同步,都需要進行系統調用。系統調用的代價相對較高:需要在user mode和kernel mode中切換。其次,每個LWP都需要有一個內核線程支持,因此LWP要消耗內核資源(內核線程的棧空間)。因此一個系統不能支持大量的LWP。

LWP.JPG

注:

<!--[if !supportLists]-->1.   <!--[endif]-->LWP的術語是借自於SVR4/MP和Solaris 2.x。

<!--[if !supportLists]-->2.   <!--[endif]-->有些系統將LWP稱爲虛擬處理器。

<!--[if !supportLists]-->3.   <!--[endif]-->將之稱爲輕量級進程的原因可能是:在內核線程的支持下,LWP是獨立的調度單元,就像普通的進程一樣。所以LWP的最大特點還是每個LWP都有一個內核線程支持。

 

用戶線程

LWP雖然本質上屬於用戶線程,但LWP線程庫是建立在內核之上的,LWP的許多操作都要進行系統調用,因此效率不高。而這裏的用戶線程指的是完全建立在用戶空間的線程庫,用戶線程的建立,同步,銷燬,調度完全在用戶空間完成,不需要內核的幫助。因此這種線程的操作是極其快速的且低消耗的。

Uthread1.JPG

上圖是最初的一個用戶線程模型,從中可以看出,進程中包含線程,用戶線程在用戶空間中實現,內核並沒有直接對用戶線程進程調度,內核的調度對象和傳統進程一樣,還是進程本身,內核並不知道用戶線程的存在。用戶線程之間的調度由在用戶空間實現的線程庫實現。

這種模型對應着恐龍書中提到的多對一線程模型,其缺點是一個用戶線程如果阻塞在系統調用中,則整個進程都將會阻塞。

加強版的用戶線程——用戶線程+LWP

這種模型對應着恐龍書中多對多模型。用戶線程庫還是完全建立在用戶空間中,因此用戶線程的操作還是很廉價,因此可以建立任意多需要的用戶線程。操作系統提供了LWP作爲用戶線程和內核線程之間的橋樑。LWP還是和前面提到的一樣,具有內核線程支持,是內核的調度單元,並且用戶線程的系統調用要通過LWP,因此進程中某個用戶線程的阻塞不會影響整個進程的執行。用戶線程庫將建立的用戶線程關聯到LWP上,LWP與用戶線程的數量不一定一致。當內核調度到某個LWP上時,此時與該LWP關聯的用戶線程就被執行。
Uthread2.JPG

小結:

很多文獻中都認爲輕量級進程就是線程,實際上這種說法並不完全正確,從前面的分析中可以看到,只有在用戶線程完全由輕量級進程構成時,纔可以說輕量級進程就是線程。

 

http://www.cnitblog.com/tarius.wu/articles/2277.html

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章