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