【Linux系统编程】fork() 函数详解
需要的頭文件:
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
功能:
用于從一個已存在的進程中創建一個新進程,新進程稱為子進程,原進程稱為父進程。
參數:
無
返回值:
成功:子進程中返回 0,父進程中返回子進程 ID。pid_t,為無符號整型。
失敗:返回 -1。
失敗的兩個主要原因是:
1)當前的進程數已經達到了系統規定的上限,這時 errno 的值被設置為 EAGAIN。
2)系統內存不足,這時 errno 的值被設置為 ENOMEM。
測試示例如下:
#include <stdio.h> #include <unistd.h>int main(void) {fork();// 獲取進程號 printf("pid = %d\n", getpid());return 0; }運行結果如下:
從運行結果,我們可以看出,fork() 之后的打印函數打印了兩次,而且打印了兩個進程號,這說明,fork() 之后確實創建了一個新的進程,新進程為子進程,原來的進程為父進程。
那子進程長什么樣的呢?
使用 fork() 函數得到的子進程是父進程的一個復制品,它從父進程處繼承了整個進程的地址空間:包括進程上下文(進程執行活動全過程的靜態描述)、進程堆棧、打開的文件描述符、信號控制設定、進程優先級、進程組號等。子進程所獨有的只有它的進程號,計時器等(只有小量信息)。因此,使用 fork() 函數的代價是很大的。簡單來說,?一個進程調用 fork() 函數后,系統先給新的進程分配資源,例如存儲數據和代碼的空間。然后把原來的進程的所有值都復制到新的新進程中,只有少數值與原來的進程的值不同。相當于克隆了一個自己。
實際上,更準確來說,Linux 的 fork() 使用是通過寫時拷貝 (copy- on-write) 實現。寫時拷貝是一種可以推遲甚至避免拷貝數據的技術。內核此時并不復制整個進程的地址空間,而是讓父子進程共享同一個地址空間。只用在需要寫入的時候才會復制地址空間,從而使各個進行擁有各自的地址空間。也就是說,資源的復制是在需要寫入的時候才會進行,在此之前,只有以只讀方式共享。
子進程是父進程的一個復制品,可以簡單認為父子進程的代碼一樣的。那大家想過沒有,這樣的話,父進程做了什么事情,子進程也做什么事情(如上面的例子),是不是不能實現滿足我們實現多任務的要求呀,那我們是不是要想個辦法區別父子進程呀,這就通過 fork() 的返回值。
fork() 函數被調用一次,但返回兩次。兩次返回的區別是:子進程的返回值是 0,而父進程的返回值則是新子進程的進程 ID。
測試示例如下:
#include <stdio.h> #include <unistd.h> #include <sys/types.h>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; }運行結果如下:
通過運行結果,可以看到,父子進程各做一件事(各自打印一句話)。這里,我們只是看到只有一份代碼,實際上,fork() 以后,有兩個地址空間在獨立運行著,有點類似于有兩個獨立的程序(父子進程)在運行著。需要注意的是,在子進程的地址空間里,子進程是從 fork() 這個函數后才開始執行代碼。
一般來說,在 fork() 之后是父進程先執行還是子進程先執行是不確定的。這取決于內核所使用的調度算法。
下面的例子,為驗證父子進程各自的地址空間是獨立的:
#include <stdio.h> #include <unistd.h> #include <sys/types.h>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; }運行結果如下:
通過得知,在子進程修改變量 a,b 的值,并不影響到父進程 a,b 的值。
在以后的編程中,fork() 之后最好是加上判斷,區別哪個是子進程的空間,哪個為父進程的空間,否則,fork() 以后的代碼為父子進程各有一份,對于用戶而言,沒有太大意義,如例子1。
總結
以上是生活随笔為你收集整理的【Linux系统编程】fork() 函数详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Linux系统编程】Linux 可执行
- 下一篇: 【Linux系统编程】进程的控制:结束进