Linux系統編程——fork() 函數詳解

需要的頭文件:

#include

#include

pid_t fork(void);

功能:

用於從一個已存在的進程中創建一個新進程,新進程稱為子進程,原進程稱為父進程。

參數:

返回值:

成功:子進程中返回 0,父進程中返回子進程 ID。pid_t,為無符號整型。

失敗:返回 -1。

失敗的兩個主要原因是:

1)當前的進程數已經達到了系統規定的上限,這時 errno 的值被設置為 EAGAIN。

2)系統內存不足,這時 errno 的值被設置為 ENOMEM。

測試示例如下:


#include
#include
#include

int main(int argc, char *argv[])
{
fork();
printf("id ==== %d\n", getpid()); // 獲取進程號

return 0;
}

運行結果如下:

Linux系統編程——fork() 函數詳解

從運行結果,我們可以看出,fork() 之後的打印函數打印了兩次,而且打印了兩個進程號,這說明,fork() 之後確實創建了一個新的進程,新進程為子進程,原來的進程為父進程。

那子進程長什麼樣的呢?

使用 fork() 函數得到的子進程是父進程的一個複製品,它從父進程處繼承了整個進程的地址空間:包括進程上下文(進程執行活動全過程的靜態描述)、進程堆棧、打開的文件描述符、信號控制設定、進程優先級、進程組號等。子進程所獨有的只有它的進程號,計時器等(只有小量信息)。因此,使用 fork() 函數的代價是很大的。


Linux系統編程——fork() 函數詳解


簡單來說, 一個進程調用 fork() 函數後,系統先給新的進程分配資源,例如存儲數據和代碼的空間。然後把原來的進程的所有值都複製到新的新進程中,只有少數值與原來的進程的值不同。相當於克隆了一個自己。

實際上,更準確來說,Linux 的 fork() 使用是通過寫時拷貝 (copy- on-write) 實現。寫時拷貝是一種可以推遲甚至避免拷貝數據的技術。內核此時並不複製整個進程的地址空間,而是讓父子進程共享同一個地址空間。只用在需要寫入的時候才會複製地址空間,從而使各個進行擁有各自的地址空間。也就是說,資源的複製是在需要寫入的時候才會進行,在此之前,只有以只讀方式共享。

子進程是父進程的一個複製品,可以簡單認為父子進程的代碼一樣的。那大家想過沒有,這樣的話,父進程做了什麼事情,子進程也做什麼事情(如上面的例子),是不是不能實現滿足我們實現多任務的要求呀,那我們是不是要想個辦法區別父子進程呀,這就通過 fork() 的返回值。

fork() 函數被調用一次,但返回兩次。兩次返回的區別是:子進程的返回值是 0,而父進程的返回值則是新子進程的進程 ID。

測試示例如下:


#include
#include
#include

int main(int argc, char *argv[])
{
pid_t pid;
pid = fork();

if( pid < 0 ){ // 沒有創建成功
perror("fork");
}

if(0 == pid){ // 子進程
while(1){
printf("I am son\n");
sleep(1);
}
}else if(pid > 0){ // 父進程
while(1){
printf("I am father\n");
sleep(1);
}
}

return 0;
}

運行結果如下:

Linux系統編程——fork() 函數詳解

通過運行結果,可以看到,父子進程各做一件事(各自打印一句話)。這裡,我們只是看到只有一份代碼,實際上,fork() 以後,有兩個地址空間在獨立運行著,有點類似於有兩個獨立的程序(父子進程)在運行著。需要注意的是,在子進程的地址空間裡,子進程是從 fork() 這個函數後才開始執行代碼

一般來說,在 fork() 之後是父進程先執行還是子進程先執行是不確定的。這取決於內核所使用的調度算法。

下面的例子,為驗證父子進程各自的地址空間是獨立的:


#include
#include
#include

int a = 10; // 全局變量

int main(int argc, char *argv[])
{
int b = 20; //局部變量
pid_t pid;
pid = fork();

if( pid < 0 ){ // 沒有創建成功

perror("fork");
}

if(0 == pid){ // 子進程
a = 111;
b = 222; // 子進程修改其值
printf("son: a = %d, b = %d\n", a, b);


}else if(pid > 0){ // 父進程
sleep(1); // 保證子進程先運行
printf("father: a = %d, b = %d\n", a, b);
}

return 0;
}

運行結果如下:

Linux系統編程——fork() 函數詳解

通過得知,在子進程修改變量 a,b 的值,並不影響到父進程 a,b 的值。

在以後的編程中,fork() 之後最好是加上判斷,區別哪個是子進程的空間,哪個為父進程的空間,否則,fork() 以後的代碼為父子進程各有一份,對於用戶而言,沒有太大意義

來源:https://blog.csdn.net/tennysonsky/article/details/45165811


分享到:


相關文章: