秒殺linux下系統調用fork()面試題 爲什麼fork執行兩次呢? http://www.itmian4.com/forum.php?mod=viewthread&tid=3248&extra=page%3D2%26filter%3Dtypeid%26typeid%3D116%26typeid%3D116 第一道題(在之前博客也寫過這道題:http://blog.csdn.net/chdhust/article/details/8535915): 題目:請問下面的程序一共輸出多少個“-”? 如果你對fork()的機制比較熟悉的話,這個題並不難,輸出應該是6個“-”,但是,實際上這個程序會很tricky地輸出8個“-”。 要講清這個題,我們首先需要知道fork()系統調用的特性,
所以,上面的那個程序爲什麼會輸入8個“-”,這是因爲printf(“-”);語句有buffer,所以,對於上述程序,printf(“-”);把“-”放到了緩存中,並沒有真正的輸出,在fork的時候,緩存被複制到了子進程空間,所以,就多了兩個,就成了8個,而不是6個。 另外,多說一下,我們知道,Unix下的設備有“塊設備”和“字符設備”的概念,所謂塊設備,就是以一塊一塊的數據存取的設備,字符設備是一次存取一個字符的設備。磁盤、內存都是塊設備,字符設備如鍵盤和串口。塊設備一般都有緩存,而字符設備一般都沒有緩存。 對於上面的問題,我們如果修改一下上面的printf的那條語句爲: 或是 就沒有問題了(就是6個“-”了),因爲程序遇到“\n”,或是EOF,或是緩中區滿,或是文件描述符關閉,或是主動flush,或是程序退出,就會把數據刷出緩衝區。需要注意的是,標準輸出是行緩衝,所以遇到“n”的時候會刷出緩衝區,但對於磁盤這個塊設備來說,“n”並不會引起緩衝區刷出的動作,那是全緩衝,你可以使用setvbuf來設置緩衝區大小,或是用fflush刷緩存。 我估計有些朋友可能對於fork()還不是很瞭解,那麼我們把上面的程序改成下面這樣: 於是,上面這段程序會輸出下面的結果,(注:編譯出的可執行的程序名爲fork) 面對這樣的圖你可能還是看不懂,沒事,我好事做到底,畫個圖給你看看:
注意:上圖中的我用了幾個色彩,相同顏色的是同一個進程。於是,我們的pstree的圖示就可以成爲下面這個樣子:(下圖中的顏色與上圖對應)
這樣,對於printf(“-”);這個語句,我們就可以很清楚的知道,哪個子進程複製了父進程標準輸出緩中區裏的的內容,而導致了多次輸出了。(如下圖所示,就是我陰影並雙邊框了那兩個子進程)
第二道題: 給出如下C程序,在linux下使用gcc編譯: #include "stdio.h"#include "sys/types.h"#include "unistd.h"int main(){ pid_t pid1; pid_t pid2; pid1 = fork(); pid2 = fork(); printf("pid1:%d, pid2:%d\n", pid1, pid2);}要求如下: 已知從這個程序執行到這個程序的所有進程結束這個時間段內,沒有其它新進程執行。 1、請說出執行這個程序後,將一共運行幾個進程。 2、如果其中一個進程的輸出結果是“pid1:1001, pid2:1002”,寫出其他進程的輸出結果(不考慮進程執行順序)。 這裏先列出一些必要的預備知識,對linux下進程機制比較熟悉的朋友可以略過。 1、進程可以看做程序的一次執行過程。在linux下,每個進程有唯一的PID標識進程。PID是一個從1到32768的正整數,其中1一般是特殊進程init,其它進程從2開始依次編號。當用完32768後,從2重新開始。 2、linux中有一個叫進程表的結構用來存儲當前正在運行的進程。可以使用“ps aux”命令查看所有正在運行的進程。 3、進程在linux中呈樹狀結構,init爲根節點,其它進程均有父進程,某進程的父進程就是啓動這個進程的進程,這個進程叫做父進程的子進程。 4、fork的作用是複製一個與當前進程一樣的進程。新進程的所有數據(變量、環境變量、程序計數器等)數值都和原進程一致,但是是一個全新的進程,並作爲原進程的子進程。 有了上面的預備知識,我們再來看看解題的關鍵。我認爲,解題的關鍵就是要認識到fork將程序切成兩段。看下圖:
上圖表示一個含有fork的程序,而fork語句可以看成將程序切爲A、B兩個部分。然後整個程序會如下運行: step1、設由shell直接執行程序,生成了進程P。P執行完Part. A的所有代碼。 step2、當執行到pid = fork();時,P啓動一個進程Q,Q是P的子進程,和P是同一個程序的進程。Q繼承P的所有變量、環境變量、程序計數器的當前值。 step3、在P進程中,fork()將Q的PID返回給變量pid,並繼續執行Part. B的代碼。 step4、在進程Q中,將0賦給pid,並繼續執行Part. B的代碼。 這裏有三個點非常關鍵: 1、P執行了所有程序,而Q只執行了Part. B,即fork()後面的程序。(這是因爲Q繼承了P的PC-程序計數器) 2、Q繼承了fork()語句執行時當前的環境,而不是程序的初始環境。 3、P中fork()語句啓動子進程Q,並將Q的PID返回,而Q中的fork()語句不啓動新進程,僅將0返回。 解題 下面利用上文闡述的知識進行解題。這裏我把兩個問題放在一起進行分析。 1、從shell中執行此程序,啓動了一個進程,我們設這個進程爲P0,設其PID爲XXX(解題過程不需知道其PID)。 2、當執行到pid1 = fork();時,P0啓動一個子進程P1,由題目知P1的PID爲1001。我們暫且不管P1。 3、P0中的fork返回1001給pid1,繼續執行到pid2 = fork();,此時啓動另一個新進程,設爲P2,由題目知P2的PID爲1002。同樣暫且不管P2。 4、P0中的第二個fork返回1002給pid2,繼續執行完後續程序,結束。所以,P0的結果爲“pid1:1001, pid2:1002”。 5、再看P2,P2生成時,P0中pid1=1001,所以P2中pid1繼承P0的1001,而作爲子進程pid2=0。P2從第二個fork後開始執行,結束後輸出“pid1:1001, pid2:0”。 6、接着看P1,P1中第一條fork返回0給pid1,然後接着執行後面的語句。而後面接着的語句是pid2 = fork();執行到這裏,P1又產生了一個新進程,設爲P3。先不管P3。 7、P1中第二條fork將P3的PID返回給pid2,由預備知識知P3的PID爲1003,所以P1的pid2=1003。P1繼續執行後續程序,結束,輸出“pid1:0, pid2:1003”。 8、P3作爲P1的子進程,繼承P1中pid1=0,並且第二條fork將0返回給pid2,所以P3最後輸出“pid1:0, pid2:0”。 9、至此,整個執行過程完畢。 所得答案: 1、一共執行了四個進程。(P0, P1, P2, P3) 2、另外幾個進程的輸出分別爲: pid1:1001, pid2:0 pid1:0, pid2:1003 pid1:0, pid2:0 進一步可以給出一個以P0爲根的進程樹: 代碼驗證結果: 第三道題: 請補足橫線,使之輸出welcome xiyoulinux if(_____) printf(“xiyoulinux ”); else printf(“welcome”); 這道題如果在if中只寫fork(),也不知道打印出來什麼順序的,所以想到函數wait(),等待子進程結束。 程序改寫爲: 運行結果: 可見這樣纔得到了正確的運行結果。 第四道題: int main() { return fork()&&fork()||fork(); } 問題:1.一共產生幾個進程 2.返回值爲1的概率爲多少?這道題一看頭都暈了,但是抓其根本,再難也都能解決的!!其實對於fork的分析,最直觀的做法也最有效,畫出進程關係圖,兩個問題便迎刃而解這裏要用到的有關fork的知識並不多,只需要知道fork的返回值:fork是一次調用,兩次返回;子進程的返回值爲0,父進程的返回值是新產生子進程的進程號(大於0)。對於&&操作符,若前操作數爲0,則直接跳過後操作數的判斷。基於以上兩個基本理論,通過圖示,即可得出結論: 圖中同一色彩的框圖代表同一進程,可見,此時共產生5個進程;結合表達式的最終值分佈情況可得,返回1的概率爲3/5 造成此種結果,最主要的誘因,是fork()後,父進程與子進程返回沒有先後順序,有時父進程先返回,有時子進程先返回。所以,根據父子進程返回的不同結果,邏輯運算符&&和||選擇是否執行其後的程序代碼(此處即爲fork)。 第五道題(EMC的一道筆試題)
這道題和第四道題比較類似,第一個fork()和最後一個fork()一定會執行,中間的同上圖,在這裏不畫了。 所以這道題解法爲:加上前面的fork和最後的fork,所有的進程都會執行,會產生4個分支,總共4*5=20個分支,也就是20個進程,除去main主進程,就是19個進程了。 |
blog 來源http://www.itmian4.com/forum.php?mod=viewthread&tid=3364&extra=page%3D1%26filter%3Dtypeid%26typeid%3D116%26typeid%3D116
blog 來源http://www.itmian4.com/forum.php?mod=viewthread&tid=3364&extra=page%3D1%26filter%3Dtypeid%26typeid%3D116%26typeid%3D116