Linux進程fork,exec,vfork詳解

    在Unix/Linux系統下進程創建時需要進行如下系統調用:fork/exec

    fork()把一個進程複製成二個進程:parent (old PID), child (new PID)

    exec()用新程序來重寫當前進程:PID沒有改變

    接下來就重點學習這兩個系統調用:

    當我們fork() 創建一個繼承的子進程將會發生如下事情:複製父進程的所有變量和內存,複製父進程的所有CPU寄存器(有一個寄存器例外)(這個寄存器是用來區分父進程和子進程的PID)

    fork()的返回值:調用fork()函數成功時,將會有兩個返回值。子進程的fork()返回0,父進程的fork()返回子進程標識符。fork() 返回值可方便後續使用,子進程可使用getpid()獲取PID

    fork()執行過程對於子進程而言,是對父進程地址空間的一次複製。下面我們通過圖示來看一下這個複製過程:

   

    注意了,圖示中的兩個childPID的值是不同的,對於父進程中的childPID當然是子進程的PID,而子進程中的childPID的值爲0。

    fork()使用示例: 

int  main()
{
	pid_t  pid;
	int  i;

	for(i=0;  i<LOOP;  i++)
    { 
           /* 創建新進程*/
    	pid = fork();
        if  (pid < 0) { /*創建失敗  */
        fprintf(stderr, “Fork Failed”);
            exit(-1);
        }
        else if (pid == 0) { /* 子進程 */
            fprintf(stdout,  “i=%d,  pid=%d,  parent  pid=%d\n”,I,      
                    getpid() ,getppid());
        }   
      }
    wait(NULL);
    exit(0);
} 


    瞭解了fork()之後我們再來看看exec()系統調用:系統調用exec( )加載新程序取代當前運行進程,也就是說exec調用成功時,它是相同的進程,但是運行了不同的程序代碼段、堆棧和堆(heap)等也都完全重寫了。它允許進程“加載”一個完全不同的程序,並從main開始執行,而我們的fork()創建出來的子進程是從fork()之後的代碼段處開始執行的。

    我們接下來討論一下fork()的實現開銷:當我們使用fork()系統調用時,首先要對子進程分配內存,然後複製父進程的內存和CPU寄存器到子進程裏。開銷昂貴!!

    然而在99%的情況裏,我們在調用fork()之後調用exec(),因爲在大多數情況下我們都是不希望和父進程執行同樣的代碼,不然的話我們創建一個新的進程也就沒有多大的意義了。這時候我們就該考慮一下這個問題了:既然不想使用父進程中的代碼,在fork()操作中內存複製就是沒有作用的。那麼爲什麼不能結合它們在一個調用中?

    此後,就產生了vfork():創建進程時,不再創建一個同樣的內存映像。一些時候稱之爲輕量級fork(),子進程幾乎立即調用exec()。而在子進程調用exec()之前,暫時與父進程共享地址空間


    我們注意到了,前面fork()的示例代碼中使用了wait()函數,那麼這個函數是幹什麼用的呢?這就要引出一個新的概念了:父進程等待子進程

    wait()系統調用用於父進程等待子進程的結束:子進程結束時通過exit()向父進程返回一個值,父進程通過wait()接受並處理返回值

    wait()系統調用的功能:有子進程存活時,父進程進入等待狀態,等待子進程的返回結果。當某子進程調用exit()時,喚醒父進程,將exit()返回值作爲父進程中wait的返回值

    既然子進程結束時是通過exit()向父進程返回一個值,那麼我們又要學習一下exit()系統調用了:

    進程結束執行時調用exit(),完成進程資源回收

    exit()系統調用的功能:

  •     將調用參數作爲進程的“結果”
  •     關閉所有打開的文件等佔用資源
  •     釋放內存
  •     釋放大部分進程相關的內核數據結構
  •     檢查是否父進程是存活着的,如存活,保留結果的值直到父進程需要它,進入殭屍(zombie/defunct)狀態。如果沒有,它釋放所有的數據結構,進程結果
  •     清理所有等待的殭屍進程

    說到這裏,大家可能對殭屍進程有點疑惑:所謂的殭屍進程,就是父進程沒有調用wait收集子進程的退出狀態,子進程就已經退出了,此時這個退出的子進程就成爲了殭屍進程,它的很多進程資源都還沒有釋放(例如PID在進程表中仍然存在)。   

發佈了58 篇原創文章 · 獲贊 1169 · 訪問量 37萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章