1. 程式人生 > >[回溯本源] Unix Fork和Windows CreateProcess可以比較嗎?

[回溯本源] Unix Fork和Windows CreateProcess可以比較嗎?

程序是現代作業系統的一個最基本的概念。書本上說:程序是一個具有獨立功能的程式關於某個資料集合的一次執行活動。它可以申請和擁有系統資源,是一個動態的概念,是一個活動的實體。它不只是程式的程式碼,還包括當前的活動,通過程式計數器的值和處理暫存器的內容來表示。

詳細點說:程序的概念主要有兩點:

第一,程序是一個實體。每一個程序都有它自己的地址空間,一般情況下,包括文字區域(text region)、資料區域(data region)和堆疊(stack region)。文字區域儲存處理器執行的程式碼;資料區域儲存變數和程序執行期間使用的動態分配的記憶體;堆疊區域儲存著活動過程呼叫的指令和本地變數。

第二,程序是一個“執行中的程式”。程式是一個沒有生命的實體,只有處理器賦予程式生命時,它才能成為一個活動的實體,我們稱其為程序。

在Unix系統中,我們通常用fork來建立一個程序,相應的,在Windows作業系統裡,我們用的是CreateProcess,一些對兩個平臺不熟悉的程式設計師,常常誤以為二者是等同的,並得出了諸如在Windows建立程序比在Unix慢許多等錯誤結論。

[b]fork時發生了什麼?[/b]
當fork()系統呼叫發生時,子程序會拷貝其父程序的所有頁面,並將其載入入作業系統為它分配的一片獨立記憶體中。這些拷貝的動作很消耗時間,而且在某些情況下並不需要這麼做。如果子程序馬上執行了"exec"系統呼叫(用來執行任何可執行檔案)或者Fork()之後就退出程序,拷貝父程序的頁面就很不划算,因為exec後包括程式碼段,資料段和堆疊等都已經被新的內容取代,只留下程序ID等一些表面上的資訊仍保持原樣,而如果fork完之後我們馬上就呼叫exec,這些辛辛苦苦拷貝來的東西又會被立刻抹掉。

在這種情況下,一種叫copy-on-write (隨拷隨寫)(COW)的技術被採用了,當fork發生時,父程序的頁面並沒有被拷貝到子程序中,相反,這些頁面被父程序和子程序所共享。無論父子程序中誰要去修改頁面,系統就為該程序拷貝一個獨立的特定頁面,然後再對其進行修改。該程序以後就只使用這個新拷貝的頁面而不再是共享的那個,而別的程序則繼續使用共享的頁面。這項技術就叫隨拷隨寫,因為當有程序要寫頁面的時候,就需要先拷貝頁面。

採用了COW技術,Fork時,子程序只需要拷貝父程序的頁面表就可以了。產生這種設計是因為有時相容POSIX的作業系統在Fork之後,並不需要執行Exec,比如apache Web Server就因此而受益,這時候的fork讓你想起點什麼?恩,有點接近Windows的CreateThread。

[code]
socket_accept()
fork()
if (child)
handleRequest()
else
goOnBeeingParent()
[/code]

COW技術使得建立子程序的代價小了許多,但是現實情況下,很多時候Fork會緊跟著一個EXEC,因為Exec必須裝載所有的映像,unix還是得花很大的代價來建立一個程序。

闡述到這裡,比較公平的比較是 Fork近似於NtCreateThread 而CreateProcess 近似於 fork + execve.

這裡為什麼說比較公平而已,因為還有別的因素我們還未涉及。

相對於Unix,Windows的設計更有彈性,它是一個多層次的而且更加元件化的作業系統,Windows擁有許多子系統,我們通常說的Windows,只是它的子系統之一,稱為WoW(Windows On Windows),其他子系統還包括Wow64,Posix和OS2。 Windows NT核心也支援COW fork,但是隻為SFU(Microsoft's UNIX environment for Windows)所使用,SFU程序和Win32程序是不同的東西。

回到Win32的程序建立上來,你會發現Windows為Win32程序建立的過程添加了許多枝節。首先,它需要通知CSRSS程序被建立,CSRSS又呼叫了LPC,而它要求至少kernel32(NTDll.dll)等動態庫要被載入,然後它又要處理許多預保留的工作專案,之後該程序才能被認為是一個Win32程序,之後還有許多枝節要去處理比如解析manifests,程式相容性檢查,程式的限制策略等等等等,這些附加在原始程序建立過程之後枝節,無疑拖累了程序建立速度。

這些巴拉巴拉的事情,讓我想起了struts,一個又一個filter被插入一次http request之後。

MS提供了一個組策略可以禁用相容性檢查,這樣可以大大提高程序的建立速度。此外Win32的執行庫(kernel32.dll等等)還帶來了大量登錄檔讀操作和初始化。這些東西對 UNIX, SFU或者原生程序都是不存在的。

不帶任何子系統的原生程序的建立速度是很快的,而建立SFU程序要比Win32程序簡單得多,也快得多,儘管Win32花了許多力氣在載入這些枝節之上,但是一方面,它提高了對客戶的友好,另一方面,執行庫的預載入使得圖形介面的處理速度更快,或者Win32程序天生就是為圖形處理做準備的。

執行在Windows裡Unix Subsystem之上的XClock.

[img]http://dl.iteye.com/upload/attachment/249124/6a142323-47fb-3ebf-a19f-2c919a278f14.png[/img]

SFU不是unix模擬系統:

[img]http://dl.iteye.com/upload/attachment/265836/cb61812b-75d9-33b4-b032-7689774537cb.gif[/img]