主頁 > 知識庫 > Unix/Linux fork隱藏的開銷

Unix/Linux fork隱藏的開銷

熱門標(biāo)簽:智能手機(jī) 使用U盤裝系統(tǒng) 百度競價(jià)點(diǎn)擊價(jià)格的計(jì)算公式 美圖手機(jī) 阿里云 硅谷的囚徒呼叫中心 檢查注冊表項(xiàng) 網(wǎng)站建設(shè)

一、fork的由來

fork的思想在UNIX出現(xiàn)幾年前就出現(xiàn)了,時(shí)間大概是1963年,這比UNIX在PDP-7上的第一個(gè)版本早了6年。
1963年,計(jì)算機(jī)科學(xué)家Melvin Conway(以Conway's Law聞名于世)寫下一篇論文,正式提出了fork思想,
fork的思想最初是Conway作為一種 多處理器并行 的方案提出來的,這個(gè)想法非常有意思。簡而言之,fork思想來源于流程圖。

我們看一個(gè)普通的流程圖:

你看,流程圖的分枝處,fork-叉子,多么形象!

一個(gè)流程圖上的分支點(diǎn)分裂出來的分支顯然是邏輯獨(dú)立的,這便是可并行的前提,于是它們便可以表現(xiàn)為不同的 處理進(jìn)程(process) 的形式,當(dāng)時(shí)的表達(dá)還只是“process”這個(gè)術(shù)語,它還不是現(xiàn)代操作系統(tǒng)意義上的“進(jìn)程”的概念。

join同步點(diǎn)表現(xiàn)為多個(gè)并行處理的進(jìn)程由于某種原因不得不同步的點(diǎn),也就是多個(gè)并行流程匯合的點(diǎn),直到現(xiàn)在,在多線程編程中,這個(gè)點(diǎn)依然叫join。比如Java Thread的join方法以及pthread庫的pthread_join函數(shù)。

廣義來講,join也表示諸如臨界區(qū)等必須串行通過的點(diǎn), 減少join點(diǎn)的數(shù)量將會提高并行的效率。

我們來看看Conway論文中關(guān)于fork的原始圖示:

Conway在論文中的另一個(gè)創(chuàng)舉是,他將處理進(jìn)程(也就是后來操作系統(tǒng)中的process的概念)以及執(zhí)行該進(jìn)程的處理器(即CPU核)分離了開來,抽象出了schedule層。

大意是說、“只要滿足系統(tǒng)中的活動(dòng)處理器數(shù)量是總處理器數(shù)量和并行處理進(jìn)程的最小值即可?!?這意味著調(diào)度程序可以將多處理器系統(tǒng)的所有處理器和系統(tǒng)所有處理進(jìn)程分別看作是統(tǒng)一的資源池和消費(fèi)者,執(zhí)行統(tǒng)一調(diào)度:

在UNIX引入fork之后,這種多處理器并行的設(shè)計(jì)思想就深入到了UNIX的核心。這個(gè)思想最終也影響了UNIX以及后來的Linux,直到現(xiàn)在。
關(guān)于這個(gè)設(shè)計(jì)思想為什么可以影響UNIX這么久,我想和Conway本人的“Conway's law”不無關(guān)系,在這個(gè)law中,他提到:Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization's communication structure.

二、早期UNIX的覆蓋(overlaying)技術(shù)

接下來看UNIX fork的另一個(gè)脈絡(luò),1969年最初的UNIX用一種在現(xiàn)在看來非常奇怪的方式運(yùn)行。

一般的資料都是從UNIX v6版本開始講起,那個(gè)版本已經(jīng)是比較 “現(xiàn)代” 的版本了,所以很少有人能看到最初的UNIX是什么樣子的。即便是能查閱到的1970年的PDP-7上運(yùn)行的UNIX源碼,也是引入fork之后的版本,在那之前的最原始版本幾乎找不到了(你可能會說,那時(shí)的UNIX不叫UNIX,but who cares…)。

最初的UNIX是一個(gè)分時(shí)系統(tǒng),它只有兩個(gè)shell進(jìn)程,分別屬于兩個(gè)終端:

分時(shí)系統(tǒng)最初并不是基于進(jìn)程分時(shí)的,那時(shí)根本還沒有完整的進(jìn)程的概念,分時(shí)系統(tǒng)是針對終端分時(shí)的,而操作員坐在終端前,為了讓每個(gè)操作員在操作過程中感覺上是在獨(dú)占機(jī)器資源,每個(gè)終端享受一段時(shí)間的時(shí)間片,在該時(shí)間片內(nèi),該終端前的操作員完全享受機(jī)器,但是為了公平,超過了時(shí)間片,時(shí)間片就要給另一個(gè)終端。

就是這樣,最初的UNIX為了體現(xiàn)分時(shí)特性,實(shí)現(xiàn)了最少的兩個(gè)終端。注意,最初的UNIX沒有fork,沒有exec,甚至沒有多進(jìn)程的概念,為了實(shí)現(xiàn)分時(shí),系統(tǒng)中僅有兩個(gè)樸素的shell進(jìn)程。

事實(shí)上,最初的UNIX用只有兩個(gè)元素的表來容納所有進(jìn)程(顯然,這看起來好笑…),當(dāng)然,這里的 “表” 的概念也是抽象的樸素概念,因?yàn)楫?dāng)時(shí)的系統(tǒng)是用PDP-7的匯編寫的,還沒有后來C語言數(shù)據(jù)結(jié)構(gòu)。

我們現(xiàn)在考慮其中一個(gè)終端的shell進(jìn)程如何工作。馬上問題就來了, 這個(gè)shell進(jìn)程如何執(zhí)行別的命令程序??

如果說系統(tǒng)中最多只能容納兩個(gè)進(jìn)程,一個(gè)終端只有一個(gè)shell進(jìn)程的話,當(dāng)該終端的shell進(jìn)程執(zhí)行其它命令程序時(shí),它自己怎么辦?這個(gè)問題得思考一會兒…

注意:不要用現(xiàn)代的眼光去評價(jià)1969年的初版UNIX,按照現(xiàn)代的眼光,執(zhí)行一個(gè)程序必然要生成一個(gè)新的進(jìn)程,顯然這在初版UNIX中并不正確。

答案是根本不用產(chǎn)生新的進(jìn)程,直接將命令程序的代碼載入內(nèi)存并 覆蓋 掉shell進(jìn)程的代碼即可!當(dāng)命令執(zhí)行完后,再用shell的代碼覆蓋掉命令程序的代碼,針對單獨(dú)的終端,系統(tǒng)其實(shí)一直在執(zhí)行下面的覆蓋循環(huán)(摘自論文的Process control 章節(jié)):

然而,在fork被引入U(xiǎn)NIX之前,事實(shí)就是這樣。一個(gè)終端上一直都是那一個(gè)進(jìn)程,一會兒它執(zhí)行shell的代碼,一會兒它執(zhí)行具體命令程序的代碼,以下是一個(gè)覆蓋程序的結(jié)構(gòu)(圖片來自《FreeBSD操作系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)》一書):

然而,當(dāng)時(shí)畢竟還沒有將這個(gè)邏輯封裝成exec系統(tǒng)調(diào)用,這些都是每一個(gè)進(jìn)程顯式完成的:

  • 對于shell執(zhí)行命令程序而言,shell自己執(zhí)行disk IO來載入命令程序覆蓋掉自身;
  • 對于命令程序執(zhí)行結(jié)束時(shí),exit調(diào)用內(nèi)部執(zhí)行disk IO載入shell程序。

exec邏輯是shell程序的一部分,由于它會被所有的命令程序所使用,該邏輯也被封裝到了exit調(diào)用中。

三、fork引入U(xiǎn)NIX前的表象

1963年Melvin Conway提出了fork思想,作為在多處理器中并行執(zhí)行進(jìn)程的一個(gè)手段。

1969年湯普森版UNIX僅有兩個(gè)shell進(jìn)程,使用覆蓋(overlaying)技術(shù)執(zhí)行命令。

截止目前,我們看到的表象是:

湯普森版UNIX沒有fork,沒有exec,沒有wait,僅有的庫函數(shù)般的exit也和現(xiàn)在的exit系統(tǒng)調(diào)用大相徑庭,顯然湯普森版UNIX并非一個(gè)多進(jìn)程系統(tǒng),而只是一個(gè)可以跑的簡陋的兩終端分時(shí)系統(tǒng)!

1、UNIX fork的誕生

fork是如何引入U(xiǎn)NIX的呢?

這還要從采用覆蓋技術(shù)的湯普森版UNIX所固有的問題說起,還是看論文原文:

若要解決這些問題,很簡單的方案湯普森都想到了:

  • 保持shell進(jìn)程的駐留而不是銷毀。命令執(zhí)行時(shí),將其交換到磁盤便是了

很顯然,命令程序是不能覆蓋掉shell進(jìn)程了。解決方案是使用 “交換” 技術(shù)。

交換技術(shù)和覆蓋技術(shù)其實(shí)都是解決有限內(nèi)存的多進(jìn)程使用問題的,不同點(diǎn)在于方向不同:

  • 覆蓋技術(shù)指的是用不同的進(jìn)程磁盤映像覆蓋當(dāng)前的進(jìn)程內(nèi)存映像。
  • 交換技術(shù)指的是用將進(jìn)程的內(nèi)存映像交換到磁盤,載入一個(gè)別的進(jìn)程磁盤映像。

使用交換技術(shù)解決覆蓋的問題,意味著要?jiǎng)?chuàng)建新的進(jìn)程:

  • 在新的進(jìn)程中執(zhí)行命令程序。

UNIX需要進(jìn)行改動(dòng),兩個(gè)配額的進(jìn)程表顯然不夠用了。當(dāng)然,解決方案也并不麻煩:

要講效率,創(chuàng)造不如抄襲,創(chuàng)建新進(jìn)程的最直接的就是copy當(dāng)前shell進(jìn)程,在copy的新進(jìn)程中執(zhí)行覆蓋,命令程序覆蓋copy的新進(jìn)程,而當(dāng)前的終端shell進(jìn)程則被交換到磁盤保得全身。

覆蓋和交換相結(jié)合了,UNIX離現(xiàn)代化更近了一步!

確定了copy當(dāng)前進(jìn)程的方案后,進(jìn)一步的問題是如何來copy進(jìn)程。

現(xiàn)在要說回fork了。

Conway提出fork思想后,馬上就有了fork的實(shí)現(xiàn)原型(正如Conway自己所說,他只是提出了一個(gè)可能造就存在的想法,并沒有實(shí)現(xiàn)它),Project Genie算是實(shí)現(xiàn)fork比較完善的系統(tǒng)之一了。

Project Genie系統(tǒng)的fork不僅僅是盲目地copy進(jìn)程,它對fork的過程擁有精細(xì)的控制權(quán),比如分配多大的內(nèi)存空間,copy哪些必要的資源等等。顯然,Project Genie的fork是沖著Conway的多處理器并行邏輯去的。

還是那句話,創(chuàng)造不如抄襲,UNIX若想實(shí)現(xiàn)進(jìn)程copy,有一個(gè)現(xiàn)成的模版就是Project Genie,但是Project Genie的fork對于UNIX太過復(fù)雜,太過精細(xì)化了,UNIX顯然用不到這些精細(xì)的控制, UNIX僅僅是想讓fork出來的新進(jìn)程被覆蓋,而不是讓它去執(zhí)行什么多處理器上的并行邏輯。

換句話說,UNIX只是借用了fork的copy邏輯的實(shí)現(xiàn),來完成一件別的事。

于是,UNIX非常粗暴的實(shí)現(xiàn)了fork!即完全copy父進(jìn)程,這就是直到現(xiàn)在我們依然在使用的fork系統(tǒng)調(diào)用:

投機(jī)取巧:

  • fork本來就不是讓你用來覆蓋新進(jìn)程的,不然為何多此一舉。fork是讓你來分解程序流程得以并行處理的。

UNIX fork就此誕生!

我們再次回顧一下UNIX fork誕生之前的景象:

再來看看fork誕生之后的景象:

于是UNIX正式邁開了現(xiàn)代化建設(shè)的步伐,一直走到了今天。

2、UNIX fork-exec

關(guān)于exec,故事沒什么好講的,它事實(shí)上就是關(guān)于上述覆蓋邏輯的封裝,此后程序員不必自己寫覆蓋邏輯了,直接調(diào)用exec系統(tǒng)調(diào)用即可。

于是經(jīng)典的UNIX fork-exec序列便形成了。

3、UNIX fork/exec/exit/wait

值得一提的是,fork被引入U(xiǎn)NIX后,exit的語義發(fā)生了巨大的改變。

在原始的1969年湯普森版UNIX中,由于每一個(gè)終端有且僅有一個(gè)進(jìn)程,這意味著覆蓋永遠(yuǎn)是在shell程序和某個(gè)命令程序之間進(jìn)行的:

  • shell執(zhí)行命令A(yù):命令程序A覆蓋內(nèi)存中的shell代碼。
  • 命令A(yù)執(zhí)行結(jié)束:shell覆蓋結(jié)束的命令A(yù)的內(nèi)存代碼。

然而,在fork被引入后,雖然shell執(zhí)行某個(gè)命令依然是特定的命令程序覆蓋fork出來的shell子進(jìn)程,但是當(dāng)命令執(zhí)行完畢后,exit邏輯卻不能再讓shell覆蓋當(dāng)前命令程序了,因?yàn)閟hell從來就沒有結(jié)束過,它作為父進(jìn)程只是被交換到了磁盤而已(后來內(nèi)存到了,可以容納多個(gè)進(jìn)程時(shí),連交換都不需要了)。

那么exit將讓誰來覆蓋當(dāng)前進(jìn)程呢?

答案是不用覆蓋,按照exit的字面意思,它只要結(jié)束自己就可以了。

本著 自己的資源自己管理的責(zé)任原則 exit只需要清理掉自己分配的資源即可。比如清理掉自己的內(nèi)存空間以及一些其它的數(shù)據(jù)結(jié)構(gòu)。

對于子進(jìn)程本身而言,由于它是父進(jìn)程生成的,所以它便由父進(jìn)程來管理釋放。于是經(jīng)典的UNIX進(jìn)程管理四件套正式形成:

到此這篇關(guān)于Unix/Linux fork隱藏的開銷的文章就介紹到這了,更多相關(guān)Unix/Linux fork內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!,希望大家以后多多支持腳本之家!

標(biāo)簽:懷化 賀州 山南 黃山 煙臺 湘潭 通遼 湖北

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《Unix/Linux fork隱藏的開銷》,本文關(guān)鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話咨詢

    • 400-1100-266