linux fork

1,fork函數

#include <unistd.h>


pid_t fork(void);
                        返回值:子進程返回0,父進程中返回子進程id,出錯返回-1

    由fork創建的新進程被稱爲子進程。fork函數被調用一次,但返回兩次。兩次返回的唯一區別是子進程的返回值是0,而父進程的返回值則是新子進程的進程id。

   爲什麼fork調用一次,卻返回兩次?其實不然,是從fork 行處,子進程共享了從fork處開始的代碼指令,相當於子進程也運行了一遍後面的代碼,不過子進程返回的是0,父進程返回的是子進程的id,是不是很神奇。例如:

........

pid_t pid;

pid = fork();
if(0 == pid)
{
    //child process;
}
else if(pid > 0)
{
    //parent process
}

......//

2,父子進程,數據段和代碼段

   子進程和父進程繼續執行fork調用之後的指令。子進程是父進程的副本。例如,子進程獲得父進程的數據空間、堆和棧的副本。注意,這是子進程所擁有的副本。父子進程並不共享這些存儲空間部分。父子進程共享正文段。

3,寫時複製

   由於在fork之後經常跟隨着exec,所以現在很多的實現並不執行一個父進程數據段、堆和棧的完全複製。作爲替代,使用了寫時複製(copy-on-write,cow)技術,這些區域由父子進程共享,而且內核將它們的訪問權限改變爲只讀。如果父子進程中的任一個試圖修改這些區域,則內核只爲修改區域的那塊內存製作一個副本,通常是虛擬存儲器系統中的一“頁”。

4,舉個例子

#include <unistd.h>
#include <stdio.h>

int glob = 8;
char buf[] = "hello world\n";

int main ()
{
    pid_t fpid; //fpid表示fork函數返回的值
    int count=0;

    if(write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1)
         printf("write error\n");

    printf("before fork\n");

    fpid=fork();
    if (fpid < 0)
        printf("error in fork!");
    else if (fpid == 0) {
       printf("i am child\n");
       glob++;
       count++;
    }
    else {
        sleep(1);
        printf("i am parent\n");
    }

    printf("pid=%d, glob=%d, count=%d\n", getpid(),glob,count);
    printf("pid=%d, &glob=%p,&count=%p\n", getpid(),&glob,&count);
    return 0;
}

運行兩次,一次直接輸出到終端,一次是寫到文件中。

     可以看到父子進程中,全局變量glob和局部變量count最後的值不一樣,這樣剛好驗證了,子進程只是父進程的副本,並不共享數據段,都各自保存了一份,互不影響。這裏還有個奇怪的打印,寫到文件後,printf(“before fork\n”)打印了兩次,分別在父進程和子進程各自打印一次?

     與緩衝區有關。write函數是不帶緩衝的,在fork之前調用write ,所以其數據寫到標準輸出一次。但是,標準i/o 庫是帶緩衝的,如果標準輸出連到終端設備,則它是行緩衝的,否則它是全緩衝的。當以交互方式運行該程序時,只得到該printf 輸出的行一次,其原因是標準輸出緩衝區由換行符沖洗。但是當標準輸出重定向到一個文件時,卻得到printf輸出兩次。這是因爲,在fork之前調用了printf 一次,但當調用fork 時,該行數據仍在緩衝區中,然後在講父進程數據空間複製到子進程時,該緩衝區也被複制到子進程中。於是那時父、子進程各自有了帶該行內容的標準i/o 緩衝區。在exit 之前的第二個printf 將其數據添加到現有的緩衝區中。當每個進程終止時,最終會沖洗其緩衝區的副本。

5,文件共享

   fork 的另外一個特性需要注意:父進程的所有打開的文件描述符都被複制到子進程中。父子進程的每個相同的打開描述符共享一個文件表項。換句話來說,在子進程中操作某個文件描述符會影響父進程相應的描述符,因爲這種共享方式使父、子進程對同一文件使用了一個文件偏移量。

   所以在fork之後處理文件描述符有兩種常見的情況:

  • 父進程等待子進程完成。在這種情況下,父進程無需對描述符做任何處理。當子進程終止後,它曾進行過讀、寫操作的任一共享描述符的文件偏移量已執行了相應更新。
  • 父子進程各自執行不同的程序段。在這種情況下,在fork之後,父子進程各自關閉它們不需使用的文件描述符,這樣就不會干擾對方使用的文件描述符,該方法在網絡服務進程中經常使用。

6,fork後,父子進程區別:

  • fork的返回值
  • 進程id不同
  • 兩個進程具有不同的父進程id
  • 子進程的tms_utime、tms_stime、tms_cutime、tms_ustime均被設置爲0。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章