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