天天看點

關于fork的面試經典題

一:關于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的面試經典題

那這又是為何呢,首先我們要知道,在fork()的調用處,整個父程序空間會原模原樣地複制到子程序中,包括指令,變量值,程式調用棧,環境變量,緩沖區等等。

再去讨論就簡單了,之是以會輸出八個“+”,是因為printf(“+”)語句有buffer,是以對于上述程式,printf(“+”)把“+”放到了緩存中,在fork的時候,緩存也被複制到子程序空間,是以兩個子程序就多輸出2個“+”,一共是8個“+。

畫個小圖康康:(相同顔色表示同一個程序)

關于fork的面試經典題

然後就能清楚知道,哪一個子程序複制了父程序标準輸出緩存區裡的内容,進而導緻多次輸出了“+”。

如下圖,被标注了笑臉的就是多輸出“+”的兩個子程序。

關于fork的面試經典題

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的面試經典題

可以看出來一共有五個分支

當第一個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");
}  
           

結果如下圖:

關于fork的面試經典題

結果很美好,如我們所料,至此問題解決。

繼續閱讀