Linux中fork函数详解(附图解与代码实现)
我們先來看個(gè)代碼,判斷一下這個(gè)代碼的輸出結(jié)果會(huì)是什么樣的,先不要去看運(yùn)行結(jié)果,判斷好后再去看看是否和你的預(yù)期結(jié)果一致。
#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<string.h>int main(void) {pid_t pid;pid = fork();printf("xxxxxxxxxx\n");while (1) {sleep(1);}return 0; }———————————————————————————————————————————
運(yùn)行結(jié)果:
xxxxxxxxxx
xxxxxxxxxx
是不是和你預(yù)想的結(jié)果不太一樣呢?為什么會(huì)是輸出兩遍呢?這是什么原理呢?
抱著這樣的問題,讓我們來研究一下fork函數(shù)的奧秘吧。
fork函數(shù)
功能:創(chuàng)建一個(gè)與原來進(jìn)程幾乎完全相同的進(jìn)程
這也就代表著,父進(jìn)程可通過調(diào)用該函數(shù)創(chuàng)建一個(gè)子進(jìn)程,兩個(gè)進(jìn)程可以做完全相同的事
返回值:pid_t類型的變量,也就是進(jìn)程id類型的變量
這里有個(gè)非常讓人驚訝的地方,fork函數(shù)的返回值是2個(gè)!!!
想想自己學(xué)了那么久的編程,好像沒有返回值是兩個(gè)的函數(shù)啊。別慌,接著往下看
我們來對(duì)父進(jìn)程通過fork函數(shù)創(chuàng)建子進(jìn)程的過程做個(gè)具體的說明,上圖!
? ? ? 在上述這個(gè)圖中,當(dāng)調(diào)用fork函數(shù)時(shí),操作系統(tǒng)會(huì)從用戶態(tài)切換回內(nèi)核態(tài)來進(jìn)行進(jìn)程的創(chuàng)建,會(huì)調(diào)用fork函數(shù)中的_CREATE函數(shù)和_CLONE函數(shù)。
? ? ? 首先調(diào)用_CREATE函數(shù),子進(jìn)程進(jìn)行虛擬地址申請,在子進(jìn)程的內(nèi)核空間中進(jìn)行不完全拷貝,為什么是不完全拷貝呢?就像父親和兒子的關(guān)系一樣,你可以和你爸爸的民族,籍貫所在地一樣,但你不能和你爸的年齡,身份證號(hào)都一樣吧。PCB作為每個(gè)進(jìn)程的唯一標(biāo)識(shí)符,就像每個(gè)人的身份證一樣,是不可能完全一樣的,所以這個(gè)地方時(shí)不完全拷貝,如pid就需要自己生成。這個(gè)地方的子進(jìn)程是新生態(tài)。
? ? ? 之后調(diào)用_CLONE函數(shù),向父進(jìn)程拷貝必要資源,子進(jìn)程的用戶空間進(jìn)行完全拷貝,子進(jìn)程繼承所有父進(jìn)程資源,如臨時(shí)數(shù)據(jù)堆棧拷貝,代碼完全拷貝。
?這個(gè)時(shí)候就有善于思考的同學(xué)會(huì)發(fā)現(xiàn),并提出以下問題:
誒誒誒,你這父進(jìn)程創(chuàng)建一個(gè)子進(jìn)程,你這子進(jìn)程把你的代碼完全拷貝走了。
- 那子進(jìn)程不是把fork函數(shù)也拷貝走了嗎?
- 那子進(jìn)程不也可以通過fork函數(shù)創(chuàng)建孫線程了嗎?
- 那你這不是子又生孫,孫又生子嗎?
- 那你這不無限創(chuàng)造進(jìn)程了嗎?
- 那為什么上面的代碼的運(yùn)行結(jié)果只有兩個(gè)輸出?
考慮的非常好啊,這也是我們下面要講的問題
講解完父進(jìn)程如何通過fork函數(shù)創(chuàng)建子進(jìn)程,接下來我們就要講解父子進(jìn)程如何執(zhí)行fork函數(shù)
上圖!
?其實(shí)大體來說,我們可以將fork函數(shù)分為三步
? ? ? ? 前2步也就是父進(jìn)程通過fork函數(shù)創(chuàng)建子進(jìn)程的步驟,在執(zhí)行完_CLONE函數(shù)后,fork函數(shù)會(huì)有第一次返回,子進(jìn)程的pid會(huì)返回給父進(jìn)程。
? ? ? ? 要注意的是,在第3步中,fork函數(shù)不是由父進(jìn)程來執(zhí)行,而是由子進(jìn)程來執(zhí)行,當(dāng)父進(jìn)程執(zhí)行完_CLONE函數(shù)后,子進(jìn)程會(huì)執(zhí)行fork函數(shù)的剩余部分,執(zhí)行最后這個(gè)語句,fork函數(shù)就會(huì)有第二次返回,如果成功就返回0,失敗就返回-1。
? ? ? ? 我們就可以總結(jié)得出,父子進(jìn)程都執(zhí)行fork函數(shù),但執(zhí)行不同的代碼段,獲取不同的返回值。所以fork函數(shù)的返回值情況如下:
? ? ? ? 父進(jìn)程調(diào)用fork,返回子線程pid(>0)
? ? ? ? 子進(jìn)程調(diào)用fork,子進(jìn)程返回0,調(diào)用失敗的話就返回-1
? ? ? ? 這也就說明了fork函數(shù)的返回值是2個(gè)
可以通過下面的代碼來驗(yàn)證該過程
#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<string.h>int main(void) {//Parent Startpid_t pid;pid = fork();if (pid > 0){printf("parent running\n");while (1){sleep(1);}}else if (pid == 0){//Child Startprintf("Child Running\n");while (1){sleep(1);}//Child End}else{perror("fork call failed\n");}while (1){sleep(1);}return 0; } //Parent End運(yùn)行結(jié)果:
parent running
Child Running
另外和大家說一個(gè)小知識(shí)點(diǎn)
整個(gè)Linux操作系統(tǒng)都是由父子進(jìn)程結(jié)構(gòu)構(gòu)成
每個(gè)進(jìn)程都有創(chuàng)建者,也就是父進(jìn)程,但是有一個(gè)進(jìn)程例外,也就是init進(jìn)程
init進(jìn)程(0 or 1),init進(jìn)程是系統(tǒng)啟動(dòng)初始化后的第一個(gè)進(jìn)程
今天的學(xué)習(xí)記錄到此結(jié)束啦,咱們下篇文章見,ByeBye!
總結(jié)
以上是生活随笔為你收集整理的Linux中fork函数详解(附图解与代码实现)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 解决世界最难数独
- 下一篇: Linux中fork函数作用,深入解析L