【Linux系统编程】fork()函数详解
00. 目錄
文章目錄
- 00. 目錄
- 01. 進程創(chuàng)建函數(shù)
- 02. 父子進程結(jié)構(gòu)
- 03. 父子進程地址空間
- 04. 附錄
01. 進程創(chuàng)建函數(shù)
#include <sys/types.h> #include <unistd.h>pid_t fork(void); 功能:用于從一個已存在的進程中創(chuàng)建一個新進程,新進程稱為子進程,原進程稱為父進程。 參數(shù):無 返回值:成功:子進程中返回 0,父進程中返回子進程 ID。pid_t,為無符號整型。失敗:返回-1。失敗的兩個主要原因是:1)當(dāng)前的進程數(shù)已經(jīng)達(dá)到了系統(tǒng)規(guī)定的上限,這時 errno 的值被設(shè)置為 EAGAIN。2)系統(tǒng)內(nèi)存不足,這時 errno 的值被設(shè)置為 ENOMEM。測試代碼:
#include <stdio.h> #include <unistd.h>int main(void) {//創(chuàng)建一個子進程fork();printf("hello world\n");return 0; }測試結(jié)果:
從運行結(jié)果,我們可以看出,fork() 之后的打印函數(shù)打印了兩次,這說明,fork() 之后確實創(chuàng)建了一個新的進程,新進程為子進程,原來的進程為父進程。
02. 父子進程結(jié)構(gòu)
使用 fork() 函數(shù)得到的子進程是父進程的一個復(fù)制品,它從父進程處繼承了整個進程的地址空間:包括進程上下文(進程執(zhí)行活動全過程的靜態(tài)描述)、進程堆棧、打開的文件描述符、信號控制設(shè)定、進程優(yōu)先級、進程組號等。子進程所獨有的只有它的進程號,計時器等(只有小量信息)。因此,使用 fork() 函數(shù)的代價是很大的。
簡單來說, 一個進程調(diào)用 fork() 函數(shù)后,系統(tǒng)先給新的進程分配資源,例如存儲數(shù)據(jù)和代碼的空間。然后把原來的進程的所有值都復(fù)制到新的新進程中,只有少數(shù)值與原來的進程的值不同。相當(dāng)于克隆了一個自己。
實際上,更準(zhǔn)確來說,Linux 的 fork() 使用是通過寫時拷貝 (copy- on-write) 實現(xiàn)。寫時拷貝是一種可以推遲甚至避免拷貝數(shù)據(jù)的技術(shù)。內(nèi)核此時并不復(fù)制整個進程的地址空間,而是讓父子進程共享同一個地址空間。只用在需要寫入的時候才會復(fù)制地址空間,從而使各個進行擁有各自的地址空間。也就是說,資源的復(fù)制是在需要寫入的時候才會進行,在此之前,只有以只讀方式共享。
子進程是父進程的一個復(fù)制品,可以簡單認(rèn)為父子進程的代碼一樣的。那大家想過沒有,這樣的話,父進程做了什么事情,子進程也做什么事情(如上面的例子),是不是不能實現(xiàn)滿足我們實現(xiàn)多任務(wù)的要求呀,那我們是不是要想個辦法區(qū)別父子進程呀,這就通過 fork() 的返回值。
fork() 函數(shù)被調(diào)用一次,但返回兩次。兩次返回的區(qū)別是:子進程的返回值是 0,而父進程的返回值則是新子進程的進程 ID。
測試代碼:
#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h>int main(void) {int i = 0;pid_t pid = -1;//創(chuàng)建子進程pid = fork();if (pid < 0){perror("fork"); goto err0;}//子進程if (0 == pid){while(1){printf("child process i = %d\n", i); i++;sleep(1);if (10 == i)break;}printf("child process exit..\n");exit(0); }//父進程printf("parent process....\n");while(1){printf("parent do thing\n"); sleep(1);}return 0; err0:return 1; }測試結(jié)果如下:
通過運行結(jié)果,可以看到,父子進程各做一件事(各自打印一句話)。這里,我們只是看到只有一份代碼,實際上,fork() 以后,有兩個地址空間在獨立運行著,有點類似于有兩個獨立的程序(父子進程)在運行著。需要注意的是,在子進程的地址空間里,子進程是從 fork() 這個函數(shù)后才開始執(zhí)行代碼。
一般來說,在 fork() 之后是父進程先執(zhí)行還是子進程先執(zhí)行是不確定的。這取決于內(nèi)核所使用的調(diào)度算法。
03. 父子進程地址空間
為驗證父子進程各自的地址空間是獨立的 ,代碼如下:
#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h>int main(void) {//父子進程之間數(shù)據(jù)段不是共享int val = 88;pid_t pid = -1;//創(chuàng)建子進程pid = fork();if (pid < 0){perror("fork"); goto err0;}//子進程if (0 == pid){val++;printf("child &val = %p val = %d\n", &val, val);exit(0); }printf("parent &val = %p val = %d\n", &val, val);return 0; err0:return 1; }測試結(jié)果如下:
子進程修改了val的值,對父進程沒有影響。
04. 附錄
4.1 參考博客:【Linux系統(tǒng)編程】fork() 函數(shù)詳解
總結(jié)
以上是生活随笔為你收集整理的【Linux系统编程】fork()函数详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Linux系统编程】进程内存模型
- 下一篇: 【Linux系统编程】vfork() 函