linux下調用fork()生成進程的一些細節

概述

在linux下,可以直接調用fork()函數來生成當前進程的子進程,且子進程與父進程共享代碼段。至於具體是共享全部代碼段,還是自fork()之後的代碼段,下面的例子中有簡單的測試。

實際上,子進程相當於父進程的拷貝,二者的代碼部分完全相同,可以看作是同一段代碼的兩次執行,他們之間可以通信,而且可以根據fork()返回值的不同對代碼進行有選擇地執行。

父進程中,調用fork()時,系統會生成一個新的進程,同時把父進程的當前寄存器狀態等 拷貝到新的進程,來生成子進程。最後如果進程生成成功,則fork()返回這個子進程的進程id,供父進程查看與使用。然後父進程繼續執行接下來的語句,而子進程想要進入CPU執行,需要排隊等待。對於子進程,由於只是複製了父進程的當前棧狀態,所以其相當於是與父進程共享了fork()之後的代碼段。子進程實際上是從fork()返回返回值時候開始執行的,但其返回值爲0.

爲什麼父進程與子進程的fork()返回值不同?

首先,需要明白的是,進程(process)不僅僅是程序(program),還包括了自己的數據,及與其相關的寄存器狀態等。每個進程只有在進入CPU中的時候纔會被執行,其數據與寄存器狀態纔會改變。由於相當於CPU來說,進程數很多,所以進程大部分時間並不是在執行(executing),而是處於準備(prepared)或者等待(waiting)狀態。一般CPU會爲每個進程分配一定長度的時間片(slice),在這個時間片內,進程會被執行,如果時間片耗盡,進程必須離開CPU,等待下一次得到時間片。當進程離開CPU時候,就需要把自己當前的狀態保存下來,比如當前執行到第幾行程序(PC),各變量的值,各寄存器的值等,以便再次進入CPU可以將這些數據加載,繼續執行。總之,每個進程都有自己的數據,及寄存器狀態,且可以在非執行狀態維持不變。

函數的返回值是在寄存器exa中儲存的,當父進程調用fork()並返回結果時,是取其當前exa中的數據,而這個數據在fork()中已經被更新爲子進程的id了,所以父進程可以通過fork()知道其產生的子進程的id,當然所有進程都可以使用另一個函數getpid()知道自己的id. 

子進程方面,通過父進程調用fork()函數,子進程對父進程的當前棧進行了複製,不過其exa寄存器狀態被初始化成了0,這時候子進程還沒有被CPU執行過,當其第一次被CPU執行時,fork()的返回值由其exa寄存器的值來確定,所以爲0. 之後,兩個進程的執行代碼就相同了。

爲什麼需要產生子進程?

父進程產生子進程的一個好處是:可以加快程序的執行速度。

由於CPU每次分給每個進程的時間片是有限的,進程如果想要得到時間片,需要競爭(有相應的算法),這時,如果一段代碼使用了生成子進程的方法,則相當於可以有兩個進程執行這一段代碼,把兩個進程的功能分開,相當於被CPU執行的機率提高了1倍,速度變快。比如使用if-else語句,父進程執行if語句塊,子進程執行else語句塊。

一個例子:

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

int main()
{
    pid_t pid;
    int test;

    srand((unsigned)time(0));
    test=rand()%10;    
    sleep(2);
    pid=fork();
    if(0>pid)
    {
        printf("Creating process error! Test nr. is %d\n", test);
        return -1;
    }
    if(0==pid)
    {
        printf("This is child process, %d. Test nr. is %d\n",getpid(),test);
        sleep(2);
    }
    else
    {
        printf("Enter the parent process, Test nr. is %d\n",test);
        wait();
        printf("This is parent process, %d, the child id is %d, Test nr. is %d\n",getpid(),pid,test);
    }
    return 1;
}

結果如下:

dhlinux@ubuntu:~/test_c$ ./fork_process 
Enter the parent process, Test nr. is 3
This is child process, 3042. Test nr. is 3
This is parent process, 3041, the child id is 3042, Test nr. is 3

解釋:這段代碼包括了打算測試的fork()相關的主要部分,沒有通信與同步。

首先,產生隨機數是爲了測試子進程是否真的是從fork()處開始執行的,如果不是的話,結果處的Test nr. 值與父進程應該不同,但結果正好與之相反,同時印證了之前敘述的fork()的原理。

如果子進程生成失敗,fork()的返回值爲負,可以通過0>pid檢測出來。此爲異常處理

從結果中可以看出,父進程首先執行,直到wait()函數處,wait()函數的作用是等待直到子進程結束。然後子進程執行,最後父進程中的子進程id與子進程自己輸出的id相同。

對應到具體執行過程:父進程競爭得到時間片,進入CPU,開始執行,然後調用了fork()函數,產生了子進程,此時子進程處於等待或者準備隊列,仍然是父進程處於CPU中,直到遇到wait()函數,雖然分配的時間片沒有用完,但遇到中斷(即wait),不得不退出。直到子進程進入CPU,執行完,父進程才得以解鎖,繼續執行。

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