程序編程代碼大全 c代碼檢查工具


程序編程代碼大全 c代碼檢查工具


前面兩節簡要地從C語言源代碼層面討論了Linux系統中進程的基本概念,我們知道了Linux內核如何描述和記錄進程的資源,以及進程的五種基本狀態和進程的家族樹 。事實上,就進程管理而言,Linux還是有一些獨特之處的 。
Linux 是如何創建進程的呢?
Linux 系統中的進程創建許多操作系統都提供了專門的進程產生機制,比較典型的過程是:首先在內存新的地址空間里創建進程,然后讀取可執行程序,裝載到內存中執行 。
Linux 系統創建線程并未使用上述經典過程,而是將創建過程拆分到兩組獨立的函數中執行:fork() 函數和 exec() 函數族 。
基本流程是這樣的:首先,fork() 函數拷貝當前進程創建子進程 。產生的子進程與父進程的區別僅在與 PID 與 PPID 以及某些資源和統計量,例如掛起的信號等 。準備好進程運行的地址空間后,exec() 函數族負責讀取可執行程序,并將其加載到相應的位置開始執行 。
fork() 函數和 exec() 函數族
Linux 系統創建進程使用的這兩組函數效果與其他操作系統的經典進程創建方式效果是相似的,可能有讀者會覺得這么做會讓進程創建過于繁瑣,其實不是的,Linux 這么做的其中一個原因是為了提高代碼的復用率,這得益于 Linux 高度概括的抽象,無需再額外設計一套機制用于創建進程 。
“寫時拷貝”早期 Linux 中的 fork() 函數直接把父進程的所有資源賦值給創建出的子進程,這樣的機制自然是簡單的,但是效率卻比較低下 。
原因是顯而易見的:子進程并不一定要使用父進程的資源,或者子進程可能僅需以只讀的方式訪問父進程的資源,這時“拷貝一份資源”就純屬多余的開銷了 。
針對這樣的問題,Linux 后續版本中的 fork() 函數開始采用“寫時拷貝”機制 。寫時拷貝技術可以將拷貝需求延遲,甚至免除拷貝,減小開銷 。
“寫時拷貝”機制
具體來說就是,Linux 在調用 fork() 創建子進程時,并不著急拷貝整個進程地址空間,而是暫時讓父子進程以只讀的方式共享同一個拷貝 。拷貝動作只在子進程需要寫入時才會發生,以確保各個進程有自己獨立的內存空間 。
如果子進程用不到或者只需要讀取共享空間數據,那么拷貝動作就被省去了,Linux 就減小了開銷 。例如,系統調用 fork() 后立即調用 exec(),此時 exec() 會加載新的映像覆蓋 fork() 的地址空間,拷貝動作完全可以省去 。
事實上,fork() 函數的實際開銷就是復制父進程的頁表以及給子進程創建唯一的進程描述符 。在大多數情況下,Linux 創建進程后都會馬上運行新的可執行程序,因此“寫時拷貝”機制可以避免相當多的數據拷貝 。創建進程速度快是 Linux 系統的一個特征,因此“寫時拷貝”是一種相當重要的優化 。
創建進程時,內存地址空間里常常包含數十 MB 的數據,如果每創建一次進程,就拷貝一次數據,開銷顯然是非常大的 。
fork() 函數Linux 中的 fork() 函數其實是基于 clone() 實現的,clone() 函數可以通過一系列參數標志指定父子進程需要共享的資源,在 Linux 中輸入 man 命令可以查看 clone() 函數的C語言原型:
clone() 函數的C語言原型
以及相關的參數標志:
相關的參數標志
在Linux中,fork() 函數最終調用了 do_fork() 函數,它的C語言代碼如下,請看(do_fork() 函數的C語言代碼比較長,下面面只列出了一部分):
do_fork() 函數的C語言代碼
do_fork() 函數完成了進程創建的大部分工作,從相關的C語言源代碼可以看出,它調用了 copy_process() 函數,copy_process() 函數的C語言源代碼如下,請看:

推薦閱讀