一:關于fork的一些基本知識
1.一個程序,包括代碼、資料和配置設定給程序的資源。fork()函數通過系統調用建立一個與原來程序幾乎完全相同的程序,也就是兩個程序可以做完全相同的事,但如果初始參數或者傳入的變量不同,兩個程序也可以做不同的事。
2. 一個程序調用fork()函數後,系統先給新的程序配置設定資源,例如存儲資料和代碼的空間。然後把原來的程序的所有值都複制到新的新程序中,隻有少數值與原來的程序的值不同。相當于克隆了一個自己。
3. fork調用的一個奇妙之處就是它僅僅被調用一次,卻能夠傳回兩次,它可能有三種不同的傳回值:
1)在父程序中,fork傳回新建立子程序的程序ID;
2)在子程序中,fork傳回0;
3)如果出現錯誤,fork傳回一個負值
來一個簡單例子
#include<stdio.h>
int main(int argc, char *argv[])
{
if (fork() == 0) {
printf("Hello from child\n");
}
else {
printf("Hello from parent\n");
}
return 0;
}
運作結果是:
Hello from parent
Hello from child
(假設大家都已經掌握fork基本知識,接下來看看有趣的幾個題)
二:面試題解析
1.請問下面的程式一共輸出多少個“+”?
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
int i;
for(i=0; i<2; i++){
fork();
printf("+");
}
return 0;
}
對于剛剛掌握fork的機制的你,可能馬上會想到會輸出6個“+”,然而跑一下驚奇地發現輸出了8個“+”(有圖有真相)
那這又是為何呢,首先我們要知道,在fork()的調用處,整個父程序空間會原模原樣地複制到子程序中,包括指令,變量值,程式調用棧,環境變量,緩沖區等等。
再去讨論就簡單了,之是以會輸出八個“+”,是因為printf(“+”)語句有buffer,是以對于上述程式,printf(“+”)把“+”放到了緩存中,在fork的時候,緩存也被複制到子程序空間,是以兩個子程序就多輸出2個“+”,一共是8個“+。
畫個小圖康康:(相同顔色表示同一個程序)
然後就能清楚知道,哪一個子程序複制了父程序标準輸出緩存區裡的内容,進而導緻多次輸出了“+”。
如下圖,被标注了笑臉的就是多輸出“+”的兩個子程序。
2.請問下面的程式建立了多少程序(不包括main程序)?
#include <stdio.h>
int main(int argc, char* argv[])
{
fork();
fork() && fork() || fork();
fork();
}
本題的關鍵在于了解fork()&& fork()|| fork()語句會怎樣執行;為了遵守循序漸進的原則,我們不妨先看一個簡單一點的一個程式:
int main()
{
fork()&&fork();
printf("+");
return 0;
}
輸出為三個“+”;
還記得邏輯與“A&&B”的文法規則嗎,隻有A,B兩個操作數同時為真,結果才為真,如果A為假,則右邊被短路,即B無論為什麼都不再執行;
那麼此程式在main這個主程序中,首先執行 fork() && fork(), 左邊的fork()傳回一個非零值,根據&&的執行原則,前面的表達式為真時,後面的表達式繼續執行,故包含main的這個主程序建立了一個子程序,然後主程序繼續執行右邊fork()&& fork(),右邊fork建立一個子程序。
由于子程序會複制父程序,而且子程序會根據其傳回值繼續執行,就是說,在子程序中, fork() &&fork()這條語句左邊表達式的傳回值是0, 是以&&右邊的表達式不會執行,這時在子程序不再建立新的程序,列印“+”後退出。
即main程序建立兩個子程序,整個一共建立了3個程序。
再看有關邏輯或的一個簡單程式: |
int main()
{
fork()||fork();
printf("+");
return 0;
}
這個程式也是要先搞清楚“A||B”的執行規則,當A為真的時候,B被短路,A為假的時候才執行B。
在main這個主程序中,首先執行 fork() || fork(), 左邊的fork()傳回一個非零值,根據||的短路原則,前面的表達式為真時,後面的表達式不執行,故包含main的這個主程序建立了一個子程序,
由于子程序會複制父程序,而且子程序會根據其傳回值繼續執行,就是說,在子程序中, fork() ||fork()這條語句左邊表達式的傳回值是0, 是以||右邊的表達式fork()要執行,這時在子程序中又建立了一個程序,
即main程序->子程序->子程序,一共建立了3個程序。
再回到之前的題,我們看到第一個fork和最後一個fork肯定是會執行的。
主要在中間3個fork上,可以畫一個圖進行描述:
可以看出來一共有五個分支
當第一個fork執行後就有25=10個程序;
當到最後一個fork執行時就有210=20個程序被建立;
是以除去main主程序,最終一共建立了19個程序。
最後我們檢驗一下:
加一句語句printf(“ok\n”)看是不是如我們推理的19個程序
#include <stdio.h>
int main(int argc, char* argv[])
{
fork();
fork() && fork() || fork();
fork();
printf("ok\n");
}
結果如下圖:
結果很美好,如我們所料,至此問題解決。