日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

fork函数全解析

發(fā)布時間:2023/12/14 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 fork函数全解析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

從最簡單(基礎)的一個例子說起,應該說是最基礎而不是簡單,下面的這個最基礎的例子其實并不簡單,因為有很多細節(jié)。
我們需要從fork函數的定義開始說起:

man 手冊官方定義 this function creates a new process. The return value is the zero in the child and the process-id number of the child in the parent, or -1 upon error. 這個函數創(chuàng)建一個新的進程。在子進程中返回0,在父進程中返回子進程的進程id,發(fā)生錯誤則返回-1。

第一次看的時候非常的奇怪,一個函數返回兩次?是的,在調用fork后,fork函數后面的所有代碼會執(zhí)行兩遍。下面通過一個例子來解釋fork函數定義的含義。

#include <stdio.h> #include <stdlib.h> #include <unistd.h> /***最基礎的fork例子**/ int main(int argc, char const *argv[]) {pid_t pid;//判斷1if ((pid=fork()) < 0){perror("fork error");}//判斷2else if (pid == 0)//子進程{printf("child getpid()=%d\n", getpid());}//判斷3else if(pid > 0)//父進程{printf("parent getpid()=%d\n", getpid());}return 0; }

這是一個最基本的例子。我們先運行一下代碼。

parent getpid()=13725 child getpid()=13726

非常的神奇,兩個判斷的代碼都執(zhí)行了。這是非常不可思議的,但fork函數確實實現了這樣的功能。也就是在fork函數后面的代碼都會執(zhí)行2遍。 這就是為什么兩個判斷都會被執(zhí)行的原因。
現在來梳理一下成功fork的執(zhí)行流程
第一步: pid=fork(),如果成功那么pid就有一個非0正值。否則返回-1。
第二步: 因為pid>0,所以進入判斷3。這是在父進程。
第三步: 父進程的代碼執(zhí)行完了,程序又會把fork后面的函數再執(zhí)行一遍,此時pid的值變?yōu)?,所以進入判斷2。

這里要解釋下getpid()函數,先看下他的定義:

man手冊官方定義: DESCRIPTIONThe getpid() function shall return the process ID of the calling process.RETURN VALUEThe getpid() function shall always be successful and no return value is reserved to in‐dicate an error.getpid()獲取調用他的進程的id,如果失敗不會有返回值。

也就是說哪個進程調用getpid,就返回這個進程的pid。所以如果你想要獲得子進程的pid,那么只要在判斷2里面調用getpid就可以了。

令人迷惑的pid_t pid變量

還有一個需要解釋的就是我們自己定義的這個pid_t pid變量。這個變量非常具有迷惑性。因為在很多書上都取這個名字,好像這個變量就是進程的pid。這是錯誤的。
這個變量的真正含義應該是return value of the fork(),也就是fork函數的返回值,而且返回值并不一定就是pid,也可能是錯誤值-1。
下面是這個變量的一種錯誤用法,試圖用這個變量來輸出父子進程的pid。

int main(int argc, char const *argv[]) {pid_t pid;//判斷1if ((pid=fork()) < 0){perror("fork error");}//判斷2else if (pid == 0){printf("child pid=%d\n",pid);}//判斷3else{printf("parent pid=%d\n",pid);}return 0; }

這個一個錯誤的例子,程序的目的是試圖通過pid變量來獲取父子進程的pid。
輸出結果:

parent pid=15077 child pid=0

這種做法是完全錯誤的,不要這么干!因為這個pid變量的命名實在是太有迷惑性了。判斷2里面的pid會永遠輸出0,而判斷3里面的pid并不是父進程的pid,實際上是子進程的pid。正確的做法是通過第一個例子的getpid函數來獲取。

pid_t pid這個變量的唯一作用就是用來做三個條件判斷。
pid_t pid這個變量的唯一作用就是用來做三個條件判斷。
pid_t pid這個變量的唯一作用就是用來做三個條件判斷。
不要拿他做別的事情。也許取名叫process_status會比較好。

父子進程的調用流程

前面的例子展示了fork最基本的用法。下面通過一個例子來解釋fork函數的調用細節(jié)。

int main(){fork();//fork1fork();//fork2printf("hello\n");return 0; }

問printf一共打印了幾次?創(chuàng)建了幾個子進程?
第一個問題非常好回答,執(zhí)行一下程序就知道了。一共是輸出了4次hello字符串。為什么是4次呢?可以做下面的圖分析:

假設我們的main進程pid是1001,注意看左邊的1,2,4進程其實都是main進程1001。進程3,6是同一個進程1002。所有一共有1001,1002,1003,1004四個進程。也就是只要數葉子節(jié)點就行了。其中1個是main進程,其它3個是子進程。有多少個進程就輸出多少次hello字符串。也就是只有4,5,6,7執(zhí)行了printf。

int main(){fork();//fork1fork();//fork2fork();//fork3printf("hello\n");return 0; }

如果程序改成這樣,結果是類似的,一共有8個進程,其中一個main進程,7個子進程。如果在程序最后加上sleep函數讓進程一直存在,那么你可以在進程管理器里面查看到對應的進程和pid,如下圖。

進程管理

既然生成了子進程,那么就需要管理這些子進程,那么誰來管呢?當然是誰生成誰負責。這其中有非常多的細節(jié)。看下面這個基本例子。通過getppid(有兩個p)獲取父進程的pid。

int main(){fork();//fork1fork();//fork2printf("ppid is %d\n",getppid());printf("hello\n");return 0; }

輸出結果

ppid =4564 hello ppid =26134 hello ppid =26135 ppid =26134 hello hello

這個結果順序是隨機的,我們發(fā)現第一個ppid好像有點奇怪,另外三個pid都是差不多的。這個進程實際是main進程,他的parent是shell,因為我們的程序是在shell里面執(zhí)行的。而shell的pid是4564(每次系統(tǒng)啟動可能發(fā)生變化)。這還是比較好理解的。但有時候輸出可能是下面這種情況。

ppid =4564 hello ppid =26570 ppid =26570 hello hello ppid =1 hello

最后一個ppid是1,這是怎么回事呢?這是因為父進程在子進程結束之前先結束了。子進程沒有了父進程,變成了孤兒進程。這時候init進程就會把這個孤兒進程收為他的“養(yǎng)子”,而init進程就成了孤兒進程的養(yǎng)父。在Linux系統(tǒng)中,init進程的id為1。這也就是ppid為1的原因。
可見父進程是沒有辦法在自己消亡的時候回收子進程的。

參考:Unix/Linux fork前傳

總結

以上是生活随笔為你收集整理的fork函数全解析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。