【IT168技術文檔】
(一) 理解Linux下進程的結構
Linux下一個進程在內存裡有三部份的數據,就是“數據段”,“堆棧段”和“代碼段”,其實學過匯編 語言的人一定知道,一般的CPU象I386,都有上述三種段寄存器,以方便操作系統的運行。“代碼段”,顧名 思義,就是存放了程序代碼的數據,假如機器中有數個進程運行相同的一個程序,那麼它們就可以使用同一個代碼段。
堆棧段存放的就是子程序的返回地址、子程序的參數以及程序的局部變量。而數據段則存放程序的全局 變量,常數以及動態數據分配的數據空間(比如用malloc之類的函數取得的空間)。這其中有許多細節問題, 這裡限於篇幅就不多介紹了。系統如果同時運行數個相同的程序,它們之間就不能使用同一個堆棧段和數據 段。
(二) 如何使用fork
在Linux下產生新的進程的系統調用就是fork函數,這個函數名是英文中“分叉”的意思。為什麼取這個 名字呢?因為一個進程在運行中,如果使用了fork,就產生了另一個進程,於是進程就“分叉”了,所以這 個名字取得很形象。下面就看看如何具體使用fork,這段程序演示了使用fork的基本框架:
void main(){ int i; if ( fork() == 0 ) { /* 子進程程序 */ for ( i = 1; i < 1000; i ++ ) printf("This is child process\n"); } else { /* 父進程程序*/ for ( i = 1; i < 1000; i ++ ) printf("This is process process\n"); } }
程序運行後,你就能看到屏幕上交替出現子進程與父進程各打印出的一千條信息了。如果程序還在運行中 ,你用ps命令就能看到系統中有兩個它在運行了。
那麼調用這個fork函數時發生了什麼呢?一個程序一調用fork函數,系統就為一個新的進程准備了前述三 個段,首先,系統讓新的進程與舊的進程使用同一個代碼段,因為它們的程序還是相同的,對於數據段和堆棧 段,系統則復制一份給新的進程,這樣,父進程的所有數據都可以留給子進程,但是,子進程一旦開始運行, 雖然它繼承了父進程的一切數據,但實際上數據卻已經分開,相互之間不再有影響了,也就是說,它們之間不 再共享任何數據了。而如果兩個進程要共享什麼數據的話,就要使用另一套函數(shmget,shmat,shmdt等) 來操作。現在,已經是兩個進程了,對於父進程,fork函數返回了子程序的進程號,而對於子程序,fork函數 則返回零,這樣,對於程序,只要判斷fork函數的返回值,就知道自己是處於父進程還是子進程中。
讀者也許會問,如果一個大程序在運行中,它的數據段和堆棧都很大,一次fork就要復制一次,那麼fork 的系統開銷不是很大嗎?其實UNIX自有其解決的辦法,大家知道,一般CPU都是以“頁”為單位分配空間的, 象INTEL的CPU,其一頁在通常情況下是4K字節大小,而無論是數據段還是堆棧段都是由許多“頁”構成的, fork函數復制這兩個段,只是“邏輯”上的,並非“物理”上的,也就是說,實際執行fork時,物理空間上兩 個進程的數據段和堆棧段都還是共享著的,當有一個進程寫了某個數據時,這時兩個進程之間的數據才有了區 別,系統就將有區別的“頁”從物理上也分開。系統在空間上的開銷就可以達到最小。
一個小幽默:下面演示一個足以"搞死"Linux的小程序,其源代碼非常簡單:
void main() { for(;;) fork(); }
這個程序什麼也不做,就是死循環地fork,其結果是程序不斷產生進程,而這些進程又不斷產生新的進程 ,很快,系統的進程就滿了,系統就被這麼多不斷產生的進程"撐死了"。用不著是root,任何人運行上述程序 都足以讓系統死掉。哈哈,但這不是Linux不安全的理由,因為只要系統管理員足夠聰明,他(或她)就可以 預先給每個用戶設置可運行的最大進程數,這樣,只要不是root,任何能運行的進程數也許不足系統總的能運 行和進程數的十分之一,這樣,系統管理員就能對付上述惡意的程序了。
上一頁123下一頁查看全文 內容導航