UNIX中的進(jìn)程及線程模型是怎樣的

這篇文章將為大家詳細(xì)講解有關(guān)UNIX中的進(jìn)程及線程模型是怎樣的,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。

成都網(wǎng)站制作、成都網(wǎng)站設(shè)計(jì)中從網(wǎng)站色彩、結(jié)構(gòu)布局、欄目設(shè)置、關(guān)鍵詞群組等細(xì)微處著手,突出企業(yè)的產(chǎn)品/服務(wù)/品牌,幫助企業(yè)鎖定精準(zhǔn)用戶,提高在線咨詢和轉(zhuǎn)化,使成都網(wǎng)站營(yíng)銷成為有效果、有回報(bào)的無錫營(yíng)銷推廣。創(chuàng)新互聯(lián)公司專業(yè)成都網(wǎng)站建設(shè)十余年了,客戶滿意度97.8%,歡迎成都創(chuàng)新互聯(lián)客戶聯(lián)系。

UNIX的傳統(tǒng)傾向于將一個(gè)任務(wù)交給一個(gè)進(jìn)程全權(quán)受理,但是一個(gè)任務(wù)內(nèi)部也不僅僅是一個(gè)執(zhí)行緒,比如一個(gè)公司的所有成員,大家都在做同一件事,每個(gè)人卻只 負(fù)責(zé)一部分,粒度減小之后,所有的事情便可以同時(shí)進(jìn)行,不管怎樣,大家還都共享著所有的資源。因此就出現(xiàn)了線程。線程其實(shí)就是共享資源的不同的執(zhí)行緒。線程的語(yǔ)義和樸素的UNIX進(jìn)程是不同的。

0.原始進(jìn)程模型-著名的fork調(diào)用

樸素的UNIX進(jìn)程依托于著名的fork調(diào)用, 就是這個(gè)fork調(diào)用讓UNIX進(jìn)程和Windows進(jìn)程截然不同,也正是因?yàn)檫@個(gè)fork調(diào)用,使二者沒有兼容的余地。這個(gè)fork調(diào)用的根源有久遠(yuǎn)的 歷史。早在UNIX之前的大型操作系統(tǒng)中,它就存在了,UNIX剛出現(xiàn)的1969年,其實(shí)并未引入fork調(diào)用,當(dāng)時(shí)之有兩個(gè)固定的進(jìn)程連接兩個(gè)終端。當(dāng) fork調(diào)用引入后,進(jìn)程的數(shù)量便快速增加了,注意,此時(shí)暫且還沒有exec調(diào)用!
       在理解fork背后的哲學(xué)之前,先看一下什么是fork。fork就是叉子,由同一個(gè)叉子柄逐漸分叉,變成一把叉子,也類似那種道生一,一生二,二生三, 三生萬物。我們看到,有了fork,理論上可以生成無數(shù)的進(jìn)程,它們都可以向上回溯到相同的根!為何UNIX會(huì)采用這個(gè)模型?我們首先要理解,在還沒有 “可執(zhí)行文件”概念的時(shí)候,進(jìn)程意味著什么。
       試想程序最初是怎么錄入到計(jì)算機(jī)的。今天它們理所當(dāng)然地存在于磁盤上,作為“可執(zhí)行文件”已經(jīng)深入人心,可是在1950-1960年代初,程序都是現(xiàn)場(chǎng)錄 入的,通過原始的紙帶或者攜帶很重的磁帶,文件系統(tǒng)還沒有概念,整個(gè)紙帶,磁帶上的內(nèi)容就是計(jì)算機(jī)要執(zhí)行的程序,執(zhí)行完了,想執(zhí)行另一個(gè)程序,就要換介 質(zhì)...人們寫一個(gè)程序當(dāng)然是為了做一件不止做一次的事,因此如果可以有多個(gè)“進(jìn)程”同時(shí)執(zhí)行紙帶/磁帶上的程序,系統(tǒng)的吞吐率將大大提高,注意,多個(gè)進(jìn) 程執(zhí)行的是同一個(gè)程序!這是最樸素的分時(shí)系統(tǒng)進(jìn)程模型。fork在伯克利分時(shí)系統(tǒng)應(yīng)運(yùn)而生!fork提供了復(fù)制當(dāng)前執(zhí)行流的手段,fork出來的所有子進(jìn) 程可以方便地執(zhí)行相同的代碼。
       這個(gè)著名的fork調(diào)用深深影響了人們?nèi)绾谓忉尫謺r(shí)系統(tǒng)!自然而然在1970年代初引入了樸素的UNIX,說fork調(diào)用著名,就是因?yàn)樗S UNIX(以及類UNIX,比如Linux)至今,直接影響了UNIX的進(jìn)程模型?,F(xiàn)在總結(jié)UNIX為何采用fork調(diào)用來生成進(jìn)程。我們知道從0到1很 難,從1到2相對(duì)容易,也比較難,從2到3...就很簡(jiǎn)單了。這就是道生一,...三生萬物!1969年的UNIX中已經(jīng)有了兩個(gè)進(jìn)程,使用fork可以 超級(jí)簡(jiǎn)單地實(shí)現(xiàn)二生三,三生萬物,于是,也許是一種巧合,早先的伯克利分時(shí)系統(tǒng)的fork正好就在那里,便被托馬斯引入了UNIX。
       我想說一下為何是三生萬物而不是二生萬物。道生一這個(gè)是最難的,我們都知道。0和1是兩個(gè)極其特殊的數(shù)字,0更加特殊。2也比較特殊,但是3就很一般了, 為何2特殊呢?我不想用博弈理論來描述,只是舉一個(gè)例子,2個(gè)人在一起,聞到一股屁味,每個(gè)人都肯定能百分百確定是誰放的,如果是我,那我肯定知道,如果 我沒有放,那肯定是對(duì)方,當(dāng)然兩人一起放的幾率也是有的。但是3個(gè)人在一起的時(shí)候,除了真正放屁的那個(gè)人之外的2個(gè)人根本無法判斷這個(gè)屁到底是誰放的。這 就是3和0,1,2的本質(zhì)區(qū)別。所以三生萬物。

1.UNIX進(jìn)程模型

在UNIX伊始,進(jìn)程的概念和其史前前輩是一致的,那個(gè)時(shí) 候文件系統(tǒng)相當(dāng)不成熟,程序員關(guān)注的是執(zhí)行好不容易寫好的任務(wù)而不是編寫任務(wù)本身(首先是沒有那么大的需求,其次是信息存儲(chǔ)是一個(gè)問題,沒有互聯(lián)網(wǎng),可以 對(duì)比一下如今的AppStore...)。fork調(diào)用便直接將UNIX的進(jìn)程組織成了tree,于是:
1.0號(hào)swap/sched進(jìn)程和1號(hào)init進(jìn)程便有了特殊地位;
2.形成了誰fork誰wait并回收的模型,在tree組織中這個(gè)很重要,便于資源回收;
3.如果父進(jìn)程先退出,將所有子進(jìn)程過繼給init,這導(dǎo)致init必須存在且不容退出,總之,任何進(jìn)程不能脫離整個(gè)進(jìn)程tree。
總之,樸素的UNIX進(jìn)程就是處在一棵樹的某個(gè)節(jié)點(diǎn)的可執(zhí)行對(duì)象。注意,它是可執(zhí)行對(duì)象。
       UNIX進(jìn)程模型就是在上述基本原則上構(gòu)建的,除此之外,在外圍,UNIX延續(xù)了歇菜的Multics項(xiàng)目的shell思想,為每一個(gè)終端開放了一個(gè) shell。shell是UNIX系統(tǒng)的第二個(gè)重要特征(如果先不說文件抽象的話!),它需要fork出來的進(jìn)程exec出一個(gè)新的不同的執(zhí)行流。從以上 fork/exec的歷史上看,它們從一開始就是分離的,這就構(gòu)建了完整的UNIX進(jìn)程模型:fork+exec。
       我們看一下UNIX的進(jìn)程模型可以構(gòu)建哪些東西。早期的UNIX將進(jìn)程進(jìn)行了組織,伙同終端的概念,UNIX給出了進(jìn)程組,會(huì)話的概念。
       進(jìn)程組是相關(guān)聯(lián)的一組進(jìn)程的集合,比如管道符連接的各個(gè)命令。更多的是它們之間的關(guān)聯(lián)由用戶來解釋。會(huì)話則是進(jìn)程組的集合,會(huì)話的意義在于用戶可以方便地 讓多個(gè)進(jìn)程組以某種形式共享終端訪問權(quán)。因?yàn)樽谝粋€(gè)終端前的是一個(gè)人,他每次執(zhí)行一個(gè)操作,這個(gè)操作作用給誰就是一個(gè)問題。他可以創(chuàng)建一個(gè)會(huì)話,該會(huì)話 內(nèi)創(chuàng)建多個(gè)進(jìn)程組,他以自己的方式讓不同的進(jìn)程組輪流成為前臺(tái)進(jìn)程組從而操作它。會(huì)話和進(jìn)程組的概念可以理解成由操作員控制的分時(shí)系統(tǒng),只是調(diào)度者不再是 操作系統(tǒng),而成了終端前的操作員。和每個(gè)CPU同時(shí)只能有一個(gè)進(jìn)程運(yùn)行類似,每一個(gè)終端會(huì)話同時(shí)只能有一個(gè)前臺(tái)進(jìn)程組。
       我們可以看到,UNIX進(jìn)程模型構(gòu)建的進(jìn)程組織自然而然形成了一個(gè)分級(jí)的分時(shí)調(diào)度層次,最底層是進(jìn)程,由操作系統(tǒng)內(nèi)核調(diào)度,然后是進(jìn)程組,協(xié)作完成一個(gè)任 務(wù),組織多個(gè)進(jìn)程,由創(chuàng)建所屬會(huì)話的操作員調(diào)度。在這個(gè)分級(jí)的層次底層,所有的進(jìn)程組織成一棵tree。這就是完整的UNIX進(jìn)程模型構(gòu)建的圖景。之所以 可以構(gòu)建如此美麗的圖景,fork+exec是基本原則,fork和exec之間,給了進(jìn)程更多的控制自己的空間,如何控制自己屬于哪一個(gè)組或者會(huì)話,由 進(jìn)程自己決定而不是調(diào)用者決定,相反的例子請(qǐng)看一下Win32 API的CreateProcess?,F(xiàn)在麻煩來了,線程出現(xiàn)了,該怎么辦?如果你想知道Linux是怎么創(chuàng)造歷史的,請(qǐng)直接跳到最后。
       我之所以沒有提及任何UNIX版本對(duì)上述構(gòu)建的實(shí)現(xiàn),是因?yàn)樗枷脒h(yuǎn)比實(shí)現(xiàn)更重要,實(shí)現(xiàn)反而會(huì)拖累你構(gòu)建新的模型。本文的最后,我會(huì)說明Linux是如何調(diào)和不同的進(jìn)程模型之間的語(yǔ)義的,同時(shí)印證了UNIX進(jìn)程模型的先進(jìn)性。

2.提供資源環(huán)境的進(jìn)程模型

Windows NT雖然在很多方面都借鑒了UNIX的思想,但是在進(jìn)程模型上卻采用了一種截然不同的思路。Windows NT出生的1990年代,應(yīng)用已經(jīng)開始遍地開花,文件系統(tǒng)也已經(jīng)非常成熟,可執(zhí)行文件的概念延續(xù)自MS-DOS時(shí)代(其實(shí)UNIXv6版本就有可執(zhí)行文件 的概念,在UNIX引入exec調(diào)用之后,可執(zhí)行文件僅僅是進(jìn)程的后備資源,僅此而已),人們可以基于Win32 API開發(fā)大量不同的程序,然后讓它們分別運(yùn)行,如果你想讓一個(gè)程序執(zhí)行多次,多點(diǎn)擊它幾次便是了。
       在這樣的時(shí)代,正如本文最初所說的,執(zhí)行的粒度細(xì)化到了一個(gè)程序的內(nèi)部。一個(gè)應(yīng)用程序要完成一項(xiàng)任務(wù),需要做不同的幾件事,可能需要同時(shí)進(jìn)行這幾件事,類 似數(shù)學(xué)中的統(tǒng)籌方法。進(jìn)程,在WinNT中也可以等同于從可執(zhí)行文件中抽取出來的命名資源集合,已經(jīng)不再適合作為可執(zhí)行的對(duì)象,真正可執(zhí)行的對(duì)象成了線 程。此時(shí)的進(jìn)程只是提供了一個(gè)資源環(huán)境,線程使用這些可以共享的資源共同完成具體的事情。這種提供資源環(huán)境的進(jìn)程模型我稱為資源模型。
       在本小節(jié),我雖然以WinNT作為例子來描述另外一種進(jìn)程模型,只是因?yàn)樗鳛檫@種模型的代表比較純粹。實(shí)際上,很多的UNIX版本也在努力融合fork模型和資源模型這兩者,企圖既能繼承UNIX的語(yǔ)義,又能實(shí)現(xiàn)多線程調(diào)度。

3.兩種模型的調(diào)和

首先,fork模型和資源模型的沖突是明顯的,典型體現(xiàn)于以下兩個(gè)方面:
1.信號(hào)問題:到底哪個(gè)線程執(zhí)行信號(hào)處理;
2.fork語(yǔ)義:假設(shè)已經(jīng)運(yùn)行了一個(gè)線程,在其中執(zhí)行了fork,如何來解釋fork的是哪個(gè)執(zhí)行流;
其中第一個(gè)問題比較好解決,規(guī)定如果不是線程自身引發(fā)的異常導(dǎo)致的信號(hào),就由任意線程來處理,反之由引發(fā)異常的線程來處理。第二個(gè)問題比較棘手,棘手之處在于某個(gè)UNIX是怎么實(shí)現(xiàn)進(jìn)程模型的。
       在進(jìn)程結(jié)構(gòu)體或者u區(qū)中維護(hù)一個(gè)鏈表,保存線程控制塊指針!Oh,NO!這是怎么回事??!UNIX怎么會(huì)忘了可執(zhí)行的對(duì)象是進(jìn)程啊!如此一來,進(jìn)程豈不成 了線程的容器?直接倒向了資源模型,然而自己確實(shí)是純正的UNIX!設(shè)計(jì)LWP是一個(gè)好方案嗎?可能是,但是它引入很多的高層抽象,顯得復(fù)雜了,如果幾年 后再引入一個(gè)新的什么什么程呢?總之,任何修改樸素UNIX進(jìn)程模型的方法都不是好方法。那么用戶庫(kù)級(jí)別的線程呢?這不屬于內(nèi)核的范疇,但表現(xiàn)了內(nèi)核的無 能為力。
       拋開實(shí)現(xiàn),回到思想。我們?cè)賮砜纯催M(jìn)程,進(jìn)程組,會(huì)話之間的關(guān)系,最基本的可執(zhí)行對(duì)象是進(jìn)程,上面的進(jìn)程組,會(huì)話都是以某種組織形式對(duì)進(jìn)程集合的封裝,每 個(gè)集合都有一系列的資源可供這個(gè)集合中的進(jìn)程共享。比如會(huì)話的環(huán)境變量,進(jìn)程組的命令行變量等,線程是什么呢,線程不就是一組執(zhí)行流的集合共享內(nèi)存地址空 間嗎?明白了些什么嗎?如果不明白,我們可以把UNIX進(jìn)程模型圖景中的進(jìn)程改成調(diào)度實(shí)體,只需要在這個(gè)圖景的基礎(chǔ)上往下走一層,線程自然而然就被支持 了:
線程,線程集合,進(jìn)程組,會(huì)話...
換成調(diào)度實(shí)體的說法,就是:
調(diào)度實(shí)體,調(diào)度實(shí)體組,進(jìn)程組,會(huì)話...
就 像進(jìn)程組里面可以只有一個(gè)進(jìn)程,組ID等于進(jìn)程ID一樣,進(jìn)程里面也可以只有一個(gè)線程,線程ID就是進(jìn)程ID。一切都統(tǒng)一到這個(gè)UNIX進(jìn)程模型的圖景中 了,如果一個(gè)線程集合只有一個(gè)線程,那么我們就稱其為進(jìn)程,如果擁有不止一個(gè)線程,我們就稱這個(gè)集合為進(jìn)程,而集合的元素為線程。其實(shí),此時(shí)此刻,怎么稱 呼已經(jīng)無所謂了。
       現(xiàn)在還缺什么?缺的是如何實(shí)現(xiàn)線程集合共享內(nèi)存地址空間。傳統(tǒng)的UNIX fork模型無疑無法做到這一點(diǎn),因?yàn)樗鼪]有任何參數(shù)用來指示實(shí)現(xiàn)這種行為。于是需要稍微修改一下fork語(yǔ)義,引入一個(gè)clone調(diào)用,含有用戶可以控制的參數(shù):

int clone(int (*fn)(void *), void *child_stack,
          int flags, void *arg, ...
          /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );


用戶不但可以控制用戶棧的位置,還可以有諸多的flags可供選擇,如果要共享調(diào)用者的內(nèi)存,CLONE_VM這個(gè)標(biāo)志無疑是需要的,當(dāng)然想clone線程不僅僅需要這一個(gè)標(biāo)志,這里就不細(xì)說了,具體可以參考NPTL最新規(guī)范。

4.Linux的對(duì)UNIX進(jìn)程模型的實(shí)現(xiàn)

Linux 實(shí)現(xiàn)的線程支持非常帥,它幾乎沒有觸動(dòng)任何已經(jīng)有的task_struct結(jié)構(gòu)體,也沒有改變?nèi)魏渭扔械膄ork語(yǔ)義。它只是引入了一個(gè)PID類型,叫做 TGID,即進(jìn)程組ID。Linux中的可執(zhí)行對(duì)象就是task_struct,而且只有task_struct。每一個(gè)task_struct擁有不止 一個(gè)ID,依照這些ID的不同的解釋方式即不同的類型,將task_struct定位到一個(gè)進(jìn)程或者是一個(gè)進(jìn)程的某個(gè)線程。ID類型如下所示:

enum pid_type
{
    PIDTYPE_PID,   
    PIDTYPE_TGID, 
    PIDTYPE_PGID,
    PIDTYPE_SID,
    PIDTYPE_MAX
};


其中:
PIDTYPE_PID:調(diào)度實(shí)體ID。如果該task_struct是一個(gè)進(jìn)程的線程,那么它就是線程ID,如果該進(jìn)程只有唯一的線程,那么它同時(shí)也是進(jìn)程ID;
PIDTYPE_TGID,:線程集合ID。如果該task_struct所屬的進(jìn)程擁有多個(gè)線程,它就是進(jìn)程ID,如果只有一個(gè)線程,它等同于PIDTYPE_PID;
PIDTYPE_PGID:進(jìn)程組ID。不解釋;
PIDTYPE_SID:會(huì)話ID。不解釋。
根據(jù)上述解釋,不管一個(gè)進(jìn)程擁有一個(gè)線程還是擁有多個(gè)線程,其進(jìn)程ID即PID均等于PIDTYPE_TGID標(biāo)識(shí)的ID。而PIDTYPE_PID標(biāo)識(shí)的ID則根據(jù)具體情況給予不同的解釋。具體實(shí)施如下:
1.每一個(gè)task_struct均有一個(gè)本PID命名空間內(nèi)唯一的ID標(biāo)識(shí)符,初始化時(shí)將其同時(shí)賦給進(jìn)程ID和線程ID;
2.如果該task_struct是一個(gè)進(jìn)程的第一個(gè)線程,即由標(biāo)準(zhǔn)的fork調(diào)用創(chuàng)建,那么保持1的初始化數(shù)值不變;
3.如果該task_struct不是一個(gè)進(jìn)程的第一個(gè)線程,即由帶有CLONE_VM等的clone調(diào)用創(chuàng)建,那么將當(dāng)前調(diào)用者的PIDTYPE_TGID標(biāo)識(shí)的ID覆蓋新task_struct的PIDTYPE_TGID標(biāo)識(shí)的ID;
4.關(guān)于進(jìn)程組ID以及會(huì)話ID的設(shè)置,有專門的setpgid, setpgrp,setsid等系統(tǒng)調(diào)用來完成,實(shí)現(xiàn)很類似上述進(jìn)程和線程;
5.每個(gè)task_struct中有4個(gè)pid結(jié)構(gòu)體,將這些pid結(jié)構(gòu)體而不是task_struct本身用鏈表連接起來,指示誰是進(jìn)程,誰是哪個(gè)進(jìn)程的線程,誰是哪個(gè)進(jìn)程組當(dāng)頭的組成員...
總之,在Linux中,不管是線程,還是進(jìn)程,都是使用task_struct這個(gè)結(jié)構(gòu)體,由其PID type的值的連接方式指示如何構(gòu)建UNIX進(jìn)程模型的圖景,這真的是太帥了。個(gè)人認(rèn)為還是用一張圖表示連接方式比較直觀,文字表達(dá)在這方面弱爆了:

UNIX中的進(jìn)程及線程模型是怎樣的

如 果理解了上面的圖,就會(huì)明白Linux在實(shí)現(xiàn)UNIX進(jìn)程模型方面做的是多么帥。如此精簡(jiǎn)的一個(gè)模型和Linux如此精簡(jiǎn)的實(shí)現(xiàn)正好搭配,不知為何被傳統(tǒng) 的UNIX引到了那么復(fù)雜的方向...Linux的實(shí)現(xiàn)明顯洞察到了UNIX進(jìn)程模型的層次化結(jié)構(gòu),即進(jìn)程,進(jìn)程組,會(huì)話這三個(gè)層次,如果再往下延伸一個(gè) 層次,將task_struct向下移動(dòng)到最底層,就基本繪制出了上面的圖景。

關(guān)于UNIX中的進(jìn)程及線程模型是怎樣的就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。

當(dāng)前名稱:UNIX中的進(jìn)程及線程模型是怎樣的
分享網(wǎng)址:http://bm7419.com/article18/ijhogp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制開發(fā)企業(yè)網(wǎng)站制作、外貿(mào)建站、定制網(wǎng)站、微信公眾號(hào)、網(wǎng)站內(nèi)鏈

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站網(wǎng)頁(yè)設(shè)計(jì)