線程理論

********線程理論********

*****線程概念的引入背景****

**進程

以前咱們已經瞭解了操做系統中進程的概念,程序並不能單獨運行,只有將程序裝載到內存中,系統爲
它分配資源才能運行,而這種執行的程序就稱之爲進程。程序和進程的區別就在於:程序是指令的集合,
它是進程運行的靜態描述文本;進程是程序的一次執行活動,屬於動態概念。在多道編程中,咱們容許
多個程序同時加載到內存中,在操做系統的調度下,能夠實現併發地執行。這是這樣的設計,大大提升
了CPU的利用率。進程的出現讓每一個用戶感受到本身獨享CPU,所以,進程就是爲了在CPU上實現多道編
程而提出的。


**有進程Why線程

進程有不少優勢,它提供了多道編程,讓咱們感受咱們每一個人都擁有本身的CPU和其餘資源,能夠提升
計算機的利用率。不少人就不理解了,既然進程這麼優秀,爲何還要線程呢?其實,仔細觀察就會發
現進程仍是有不少缺陷的,主要體如今兩點上:

進程只能在一個時間幹一件事,若是想同時幹兩件事或多件事,進程就無能爲力了。

進程在執行的過程當中若是阻塞,例如等待輸入,整個進程就會掛起,即便進程中有些工做不依賴於輸入
的數據,也將沒法執行。

若是這兩個缺點理解比較困難的話,舉個現實的例子也許你就清楚了:若是把咱們上課的過程當作一個
進程的話,那麼咱們要作的是耳朵聽老師講課,手上還要記筆記,腦子還要思考問題,這樣才能高效的
完成聽課的任務。而若是隻提供進程這個機制的話,上面這三件事將不能同時執行,同一時間只能作一
件事,聽的時候就不能記筆記,也不能用腦子思考,這是其一;若是老師在黑板上寫演算過程,咱們開
始記筆記,而老師忽然有一步推不下去了,阻塞住了,他在那邊思考着,而咱們呢,也不能幹其餘事,
即便你想趁此時思考一下剛纔沒聽懂的一個問題都不行,這是其二。

如今你應該明白了進程的缺陷了,而解決的辦法很簡單,咱們徹底可讓聽、寫、思三個獨立的過程,
並行起來,這樣很明顯能夠提升聽課的效率。而實際的操做系統中,也一樣引入了這種相似的機制——線程。

**線程的出現

60年代,在OS中能擁有資源和獨立運行的基本單位是進程,然而隨着計算機技術的發展,進程出現了很
多弊端,一是因爲進程是資源擁有者,建立、撤消與切換存在較大的時空開銷,所以須要引入輕型進程;
二是因爲對稱多處理機(SMP)出現,能夠知足多個運行單位,而多個進程並行開銷過大。
所以在80年代,出現了能獨立運行的基本單位——線程(Threads)。
注意:進程是資源分配的最小單位,線程是CPU調度的最小單位.
    每個進程中至少有一個線程。


****進程和線程的關係****

操做系統中包含進程,進程中包含線程

線程與進程的區別能夠概括爲如下4點:

1)地址空間和其餘資源(如打開文件):進程間互相獨立,同一進程的個線程間共享。
某進程內的線程在其餘進程不可見。
2)通訊:進程間的通訊IPC, 線程間能夠直接讀寫進程數據端(如全局變量)來進行通訊
須要進程同步和互斥手段的輔助,以保證數據的一致性。
3)調度和切換:線程上下文切換比進程上下文切換要快的多。
4)在多線程操做系統中,進程不是一個可執行的實體。


****線程額特色****

在多線程的操做系統中,一般是在一個進程中包括多個線程,每一個線程都是做爲利用PU的基本單位,是花
費最小開銷的實體。線程具備如下屬性。

1)輕型實體
線程中的實體基本上不擁有系統資源,只是有一點必不可少的、能保證獨立運行的資源。
線程的實體包括程序、數據和TCB。線程是動態概念,它的動態特性由線程控制塊TCB(Thread Control Block)描述。

TCB包括如下信息:
(1)線程狀態。
(2)當線程不運行時,被保存的現場資源。
(3)一組執行堆棧。
(4)存放每一個線程的局部變量主存區。
(5)訪問同一個進程中的主存和其它資源。
用於指示被執行指令序列的程序計數器、保留局部變量、少數狀態參數和返回地址等的一組寄存器和堆棧。

2)獨立調度和分派的基本單位。
在多線程OS中,線程是能獨立運行的基本單位,於是也是獨立調度和分派的基本單位。因爲線程很「輕」,
線程的切換很是迅速且開銷小(在同一進程中的)。

3)共享進程資源。
線程在同一進程中的各個線程,均可以共享該進程所擁有的資源,這首先表如今:全部線程都具備相同的
程id,這意味着,線程能夠訪問該進程的每個內存資源;此外,還能夠訪問進程所擁有的已打開文件、
時器、信號量機構等。因爲同一個進程內的線程共享內存和文件,因此線程之間互相通訊沒必要調用內核。

4)可併發執行。
在一個進程中的多個線程之間,能夠併發執行,甚至容許在一個進程中全部線程都能併發執行;一樣,
不一樣進程中的線程也能併發執行,充分利用和發揮了處理機與外圍設備並行工做的能力。

****使用線程的實際場景****

開啓一個字處理軟件進程,該進程確定須要辦不止一件事情,好比監聽鍵盤輸入,處理文字,定時自動
將文字保存到硬盤,這三個任務操做的都是同一塊數據,於是不能用多進程。只能在一個進程裏併發地
開啓三個線程,若是是單線程,那就只能是,鍵盤輸入時,不能處理文字和自動保存,自動保存時又不
能輸入和處理文字。


****內存中的線程****

https://images2017.cnblogs.com/blog/827651/201801/827651-20180118213709271-527249129.png

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

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

不一樣的進程之間是充滿敵意的,彼此是搶佔、競爭cpu的關係,若是迅雷會和QQ搶資源。而同一個進程是
一個程序員的程序建立,因此同一進程內的線程是合做關係,一個線程能夠訪問另一個線程的內存地址,
你們都是共享的,一個線程乾死了另一個線程的內存,那純屬程序員腦子有問題。

相似於進程,每一個線程也有本身的堆棧,不一樣於進程,線程庫沒法利用時鐘中斷強制線程讓出CPU,能夠
調用thread_yield運行線程自動放棄cpu,讓另一個線程運行。

線程一般是有益的,可是帶來了不小程序設計難度,線程的問題是:

1. 父進程有多個線程,那麼開啓的子線程是否須要一樣多的線程

2. 在同一個進程中,若是一個線程關閉了文件,而另一個線程正準備往該文件內寫內容呢?

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


**用戶級線程和內核級線程(瞭解)**


-用戶級線程

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

https://images2017.cnblogs.com/blog/827651/201801/827651-20180121020547006-752791661.png


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


-內核級線程

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

https://images2017.cnblogs.com/blog/827651/201801/827651-20180121020859803-1526586154.png


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

1)用戶級線程和內核級線程的區別


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

2)內核線程的優缺點

優勢:當有多個處理機時,一個進程的多個線程能夠同時執行。
缺點:由內核進行調度。

3)用戶級線程的缺點

優勢:
線程的調度不須要內核直接參與,控制簡單。
能夠在不支持線程的操做系統中實現。
建立和銷燬線程、線程切換代價等線程管理的代價比內核線程少得多。
容許每一個進程定製本身的調度算法,線程管理比較靈活。
線程可以利用的表空間和堆棧空間比內核級線程多。
同一進程中只能同時有一個線程在運行,若是有一個線程使用了系統調用而阻塞,那麼整個進程都會被掛起。另外,
頁面失效也會產生一樣的問題。
缺點:
資源調度按照進程進行,多個處理機下,同一個進程中的線程只能在同一個處理機下分時複用


-混合實現

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


https://images2017.cnblogs.com/blog/827651/201801/827651-20180121021033553-698190505.png

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模型,一般這種模型的用戶線程數會比內核的調度實體多。在這種實現裏,線程庫自己
必須去處理可能存在的調度,這樣在線程庫內部的上下文切換一般都會至關的快,由於它避免了系統調用轉到內核態。然
而這種模型增長了線程實現的複雜性,並可能出現諸如優先級反轉的問題,此外,用戶態的調度如何跟內核態的調度進行協
調也是很難讓人滿意。
相關文章
相關標籤/搜索