內存中的線程


一、內存中的線程

177-內存中的線程-03.png?x-oss-process=style/watermark

多個線程共享同一個進程的地址空間中的資源,是對一台計算機上多個進程的模擬,有時也稱線程為輕量級的進程。

而對一台計算機上多個進程,則共享物理內存、磁盤、打印機等其他物理資源。多線程的運行也多進程的運行類似,是CPU在多個線程之間的快速切換。

不同的進程之間是充滿敵意的,彼此是搶占、競爭CPU的關系,如果迅雷會和QQ搶資源。而同一個進程是由一個程序員的程序創建,所以同一進程內的線程是合作關系,一個線程可以訪問另外一個線程的內存地址,大家都是共享的,一個線程干死了另外一個線程的內存,那純屬程序員腦子有問題。

類似於進程,每個線程也有自己的堆棧,不同於進程,線程庫無法利用時鍾中斷強制線程讓出CPU,可以調用thread_yield運行線程自動放棄CPU,讓另外一個線程運行。

線程通常是有益的,但是帶來了不小程序設計難度,線程的問題是:

  1. 父進程有多個線程,那么開啟的子線程是否需要同樣多的線程。
  2. 在同一個進程中,如果一個線程關閉了文件,而另外一個線程正准備往該文件內寫內容呢?

因此,在多線程的代碼中,需要更多的心思來設計程序的邏輯、保護程序的數據。

二、用戶級線程和內核級線程(了解)

線程的實現可以分為兩類:用戶級線程(User-Level Thread)和內核線線程(Kernel-Level Thread),后者又稱為內核支持的線程或輕量級進程。在多線程操作系統中,各個系統的實現方式並不相同,在有的系統中實現了用戶級線程,有的系統中實現了內核級線程。

2.1 用戶級線程

內核的切換由用戶態程序自己控制內核切換,不需要內核干涉,少了進出內核態的消耗,但不能很好的利用多核CPU。

177-內存中的線程-04.png?x-oss-process=style/watermark

在用戶空間模擬操作系統對進程的調度,來調用一個進程中的線程,每個進程中都會有一個運行時系統,用來調度線程。此時當該進程獲取CPU時,進程內再調度出一個線程去執行,同一時刻只有一個線程執行。

2.2 內核級線程

內核級線程:切換由內核控制,當線程進行切換的時候,由用戶態轉化為內核態。切換完畢要從內核態返回用戶態;可以很好的利用smp,即利用多核CPU。windows線程就是這樣的。

177-內存中的線程-02.png?x-oss-process=style/watermark

2.3 用戶級與內核級線程的對比

2.3.1 用戶級線程和內核級線程的區別

  1. 內核支持線程是OS內核可感知的,而用戶級線程是OS內核不可感知的。
  2. 用戶級線程的創建、撤消和調度不需要OS內核的支持,是在語言(如Java)這一級處理的;而內核支持線程的創建、撤消和調度都需OS內核提供支持,而且與進程的創建、撤消和調度大體是相同的。
  3. 用戶級線程執行系統調用指令時將導致其所屬進程被中斷,而內核支持線程執行系統調用指令時,只導致該線程被中斷。
  4. 在只有用戶級線程的系統內,CPU調度還是以進程為單位,處於運行狀態的進程中的多個線程,由用戶程序控制線程的輪換運行;在有內核支持線程的系統內,CPU調度則以線程為單位,由OS的線程調度程序負責線程的調度。
  5. 用戶級線程的程序實體是運行在用戶態下的程序,而內核支持線程的程序實體則是可以運行在任何狀態下的程序。

2.3.2 內核線程的優缺點

優點:當有多個處理機時,一個進程的多個線程可以同時執行。

缺點:由內核進行調度。

2.3.3 用戶級線程的優缺點

  • 優點:

    • 線程的調度不需要內核直接參與,控制簡單。
    • 可以在不支持線程的操作系統中實現。
    • 創建和銷毀線程、線程切換代價等線程管理的代價比內核線程少得多。
    • 允許每個進程定制自己的調度算法,線程管理比較靈活。
    • 線程能夠利用的表空間和堆棧空間比內核級線程多。
    • 同一進程中只能同時有一個線程在運行,如果有一個線程使用了系統調用而阻塞,那么整個進程* 都會被掛起。另外,頁面失效也會產生同樣的問題。
  • 缺點:

    • 資源調度按照進程進行,多個處理機下,同一個進程中的線程只能在同一個處理機下分時復用

2.4 混合實現

用戶級與內核級的多路復用,內核同一調度內核線程,每個內核線程對應n個用戶線程。

177-內存中的線程-01.png?x-oss-process=style/watermark

2.4.1 linux操作系統的 NPTL

歷史:在內核2.6以前的調度實體都是進程,內核並沒有真正支持線程。它是能過一個系統調用clone()來實現的,這個調用創建了一份調用進程的拷貝,跟fork()不同的是,這份進程拷貝完全共享了調用進程的地址空間。LinuxThread就是通過這個系統調用來提供線程在內核級的支持的(許多以前的線程實現都完全是在用戶態,內核根本不知道線程的存在)。非常不幸的是,這種方法有相當多的地方沒有遵循POSIX標准,特別是在信號處理,調度,進程間通信原語等方面。

很顯然,為了改進LinuxThread必須得到內核的支持,並且需要重寫線程庫。為了實現這個需求,開始有兩個相互競爭的項目:IBM啟動的NGTP(Next Generation POSIX Threads)項目,以及Redhat公司的NPTL。在2003年的年中,IBM放棄了NGTP,也就是大約那時,Redhat發布了最初的NPTL。

NPTL最開始在redhat linux 9里發布,現在從RHEL3起內核2.6起都支持NPTL,並且完全成了GNU C庫的一部分。

設計:NPTL使用了跟LinuxThread相同的辦法,在內核里面線程仍然被當作是一個進程,並且仍然使用了clone()系統調用(在NPTL庫里調用)。但是,NPTL需要內核級的特殊支持來實現,比如需要掛起然后再喚醒線程的線程同步原語futex.

NPTL也是一個1*1的線程庫,就是說,當你使用pthread_create()調用創建一個線程后,在內核里就相應創建了一個調度實體,在linux里就是一個新進程,這個方法最大可能的簡化了線程的實現。

除NPTL的1*1模型外還有一個m*n模型,通常這種模型的用戶線程數會比內核的調度實體多。在這種實現里,線程庫本身必須去處理可能存在的調度,這樣在線程庫內部的上下文切換通常都會相當的快,因為它避免了系統調用轉到內核態。然而這種模型增加了線程實現的復雜性,並可能出現諸如優先級反轉的問題,此外,用戶態的調度如何跟內核態的調度進行協調也是很難讓人滿意。


免責聲明!

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



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