1. 前言
之前的小節(jié)中介紹了操作系統(tǒng)的進程,操作系統(tǒng)中有個創(chuàng)建進程的重要方法就是 fork 函數(shù),當需要執(zhí)行和本進程相關(guān)的獨立任務(wù)時,一般需要創(chuàng)建一個有血緣關(guān)系的子進程。
2. fork 函數(shù)
面試官提問: Linux 系統(tǒng)中的 fork 函數(shù)是什么,有什么用途?
題目解析:
首先從定義上看,fork 函數(shù)的作用是在一個進程的基礎(chǔ)上創(chuàng)建新的進程,原有的進程被定義為父進程,新的進程被定義為子進程。
在 C 語言中調(diào)用 fork()
函數(shù)即實現(xiàn) fork 功能,示例:
#include<unistd.h> //包含fork函數(shù)的頭文件
pid_t fork(void); //fork的返回類型為pid_t,我們可以看成int類型
認識一個函數(shù),需要從函數(shù)的定義入手,了解函數(shù)做了什么事情,入?yún)⑹鞘裁矗鰠⑹鞘裁?。我們?C 語言實現(xiàn)的一個典型的 fork 的程序入手,示例:
#include <unistd.h>
#include <stdio.h>
int main ()
{
pid_t fpid;
int count = 0;
fpid = fork();
if (fpid == 0) { //返回值是0,說明是子進程
printf("i am the child process, process id is %d\n", getpid());
count++;
} else if(fpid > 0) { //返回值>0,說明是父進程
printf("i am the parent process, process id is %d\n", getpid());
count++;
} else { //返回值<0,說明fork發(fā)生異常
printf("fork encounter exception, process id is %d\n", getpid());
}
//打印計數(shù)器
printf("after fork, counting result : %d\n", count);
return 0;
}
在 MacOS 系統(tǒng)上編譯運行案例示例代碼,運行結(jié)果如下圖。
如果是不了解函數(shù)原理的前提下,僅僅從代碼層面分析,在調(diào)用 fork()
函數(shù)之后,代碼會進入 if-else
判斷邏輯,在控制臺輸出一條語句,然后在控制臺打印計數(shù)器的數(shù)值。但是從真正執(zhí)行的結(jié)果來看,這兩個打印動作都分別執(zhí)行了兩次,并不符合我們的預(yù)期。fork 之后的代碼邏輯被執(zhí)行了兩次,而且兩次進入的不同的分支,所以重點在于 fork 函數(shù)到底有啥作用。
按照定義、入?yún)⒑统鰠⑷阶叩目蚣?,首先是分析函?shù)的定義,調(diào)用 fork 函數(shù)之后發(fā)生了什么事情:
(1)分配內(nèi)存:分配新的內(nèi)存空間給子進程;
(2)拷貝數(shù)據(jù):拷貝父進程的數(shù)據(jù)結(jié)構(gòu)給子進程;
(3)加入列表:將新生成的子進程添加到操作系統(tǒng)的進程列表;
(4)返回結(jié)果:fork 函數(shù)調(diào)度并且返回。
然后是分析 fork 函數(shù)的入?yún)ⅲ琭ork 函數(shù)入?yún)⑹?void,也就是自動同步進程的上下文,不需要手動聲明。
最后是分析 fork 函數(shù)的出參,fork 函數(shù)和程序員日常接觸的函數(shù)不同,我們在 C 或者 Java 中定義的函數(shù)只會有一個返回值,fork 函數(shù)則是調(diào)用一次,返回二次。調(diào)用方(例如上述案例的 main 函數(shù))根據(jù)返回值的不同判斷處于父進程還是子進程。
(1)返回值 < 0:調(diào)用失敗,一般是因為操作系統(tǒng)中的進程個數(shù)達到上限或者內(nèi)存不足以分配給新的進程;
(2)返回值 = 0:調(diào)用成功,并且處于子進程;
(3)返回值 > 0:調(diào)用成功,并且處于父進程。
現(xiàn)在就不難理解,從調(diào)用 fork()
函數(shù),代碼實際上是被父子進程分別執(zhí)行了一次,父進程的進程 id 是 52331,子進程的進程 id 是 52332。
在掌握原理之后,我們繼續(xù)探究 fork 函數(shù)的應(yīng)用場景。fork 函數(shù)的本質(zhì)在原有的進程基礎(chǔ)上創(chuàng)建一個新的進程,所以在網(wǎng)絡(luò)通信中使用較多,例如在客戶端發(fā)送一個 HTTP 請求打到服務(wù)器時,服務(wù)器進程 fork 出一個子進程用于處理單個請求,父進程則繼續(xù)等待其他的請求。
3. 小結(jié)
本章節(jié)介紹了 Linux 的 fork 函數(shù),fork 如同其英文名,就是進程的分叉。fork 函數(shù)簡化了操作系統(tǒng)的進程管理,又提供了一個簡單的多進程生成方案,在操作系統(tǒng)中的地位非常核心,候選人需要注意 fork 函數(shù)調(diào)用 1 次,返回 2 次的核心特性以及返回值。