Linux下Fork與Exec使用


Linux下進程的結構

  Linux下一個進程在內存里有三部分的數據,就是"代碼段"、"堆棧段"和"數據段"。其實學過匯編語言的人一定知道,一般的CPU都有上述三種段寄存器,以方便操作系統的運行。這三個部分也是構成一個完整的執行序列的必要的部分。

  "代碼段",顧名思義,就是存放了程序代碼的數據,假如機器中有數個進程運行相同的一個程序,那么它們就可以使用相同的代碼段。

  "堆棧段"存放的就是子程序的返回地址、子程序的參數以及程序的局部變量

  而數據段則存放程序的全局變量,常數以及動態數據分配的數據空間(比如用malloc之類的函數取得的空間)。

Linux下的進程控制

在傳統的Unix環境下,有兩個基本的操作用於創建和修改進程:函數fork( )用來創建一個新的進程,該進程幾乎是當前進程的一個完全拷貝;函數族exec( )用來啟動另外的進程以取代當前運行的進程。

  fork()

  fork在英文中是"分叉"的意思。為什么取這個名字呢?因為一個進程在運行中,如果使用了fork,就產生了另一個進程,於是進程就"分叉"了,所以這個名字取得很形象。

  調用這個fork函數時發生了什么呢?fork函數啟動一個新的進程,這個進程幾乎是當前進程的一個拷貝子進程和父進程使用相同的代碼段子進程復制父進程的堆棧段和數據段。個人理解,還拷貝了環境變量。這樣,父進程的所有數據都可以留給子進程,但是,子進程一旦開始運行,雖然它繼承了父進程的一切數據,但實際上數據卻已經分開,相互之間不再有影響了,也就是說,它們之間不再共享任何數據了。它們再要交互信息時,只有通過進程間通信來實現。

  也就是說,fork出的子進程的一切都來自父進程,包括代碼、數據、堆棧、打開的文件等,就連代碼的執行位置(狀態)都是一樣的。

   exec( )函數族

  一個進程一旦調用exec類函數,它本身就"死亡"了,系統把代碼段替換成新的程序的代碼,廢棄原有的數據段和堆棧段,並為新程序分配新的數據段與堆棧段,並重新加載配置文件。唯一留下的,就是進程號,個人理解,還留下了拷貝自父進程環境變量和重新加載的配置文件中的內容(環境變量,函數等等)

 

我們平時使用shell命令時,產生的子進程方式大體有以下兩種

第一種只使用 fork() 函數,子進程幾乎是當前進程的一個拷貝,父進程中的函數、全局變量、別名以及環境變量在子進程中仍然有效。不會重新加載配置文件

  注意:子進程只是父進程的拷貝,兩者並不共享,所以在子進程中改變了某個全局變量或者環境變量,不會反應到父進程中,父進程中該變量的值保持原樣。同樣,在子進程被創建后,在改變父進程中變量的值,也不會反應到子進程中。

 

第二種使用 fork() 后,又使用了 exec() 函數。這樣新建子進程只留下了進程ID和環境變量,及重新加載的配置文件。其余的全局變量,別名等就不存在了。相當於exce對子進程進行了格式化一樣

 

以新進程的方式運行腳本文件,比如bash ./test.shchmod +x ./test.sh; ./test.sh,或者在當前 Shell 中使用 bash 命令啟動新的 Shell,它們都屬於第二種創建子進程的方式,所以子進程除了能繼承父進程的環境變量外,基本上也不能使用父進程的什么東西了,比如,父進程的全局變量、局部變量、文件描述符、別名等在子進程中都無效。

但是,組命令、命令替換、管道這幾種語法都使用第一種方式創建進程,所以子進程可以使用父進程的一切,包括全局變量、局部變量、別名等。

 

為了方便兩者的區別,我們稱第一種為子shell,第二種為子進程。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM