爲了暑期的騰訊實習,拿到offer以後一直在linux的道路上不斷學習。
這兩天逛了下酷殼大神的blog(http://coolshell.cn/articles/7965.html),偶然看到一個關於fork小問題,雖然以前想通了,不過仍是值得回味而且和你們分享下的。html
你們有興趣能夠想一想,下面輸出了多少個「g」?linux
#include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(void) { int i; for(i=0; i<2; i++){ fork(); printf("g"); } wait(NULL); wait(NULL); return 0; }
也許你很快就說,那麼簡單,第一次循環,fork後2個進程,第二次再fork出4個,一共6個進程,確定是6個「g」面試
恭喜你,若是我是面試官,那麼你已經跪了 = =shell
答案:會輸出8個「g」bash
有一個很重要的東西是,在fork()的調用處,整個父進程空間會原模原樣地複製到子進程中,包括指令,變量值,程序調用棧,環境變量,緩衝區,等等。oop
等等!!什麼,你說所有複製過去?那麼我能夠解釋爲:
上面的那個程序爲何會輸入8個「-」,這是由於printf(「-「);語句有buffer學習
咱們來試試printf的緩衝區:spa
#include <stdio.h> #include <unistd.h> //for sleep() int main(void) { printf("/nstart the dead loop/n"); while(1) { printf("/b->"); fflush(stdout);//刷新輸出緩衝區 usleep(100000); } return 1; }
哦哦!!若是不作fflush這個動做,上邊的輸出便不會顯示到屏幕上咯?
除非其中有換行操做或者緩衝區,這也許就是所謂的「到終端行規程」!code
呃呃,不要脫離了重點,咱們仍是回到剛纔的fork問題上~htm
既然咱們知道了printf有緩衝區,那麼,又如何?
容我引用下酷殼大神美麗的圖解:
你能夠清楚的看到,用陰影框標註出來的兩個「最終版子進程(i=2)」
他們在fork拷貝的時候,就將 parent的緩衝區一併拷貝過去,這時「g」還在緩衝區
那麼,在輸出的時候,兩個子進程才一併把「g」從緩衝區輸出來,那麼是否是兩個子進程沒人多輸出了一個?
1+1+1+1+12+12=8個
OK,我知道你和我同樣可能有點懵懵的,我改下程序咯~
#include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(void) { int i; for(i=0; i<2; i++){ fork(); printf("g ppid=%d, pid=%d, i=%d ", getppid(), getpid(), i); } wait(NULL); wait(NULL); return 0;
你能夠gcc下這個程序,在個人電腦裏輸出是這樣的:
g ppid=3082, pid=3128, i=0 g ppid=3128, pid=3130, i=1 g ppid=3128, pid=3129, i=0 g ppid=3129, pid=3131, i=1 g ppid=3128, pid=3129, i=0 g ppid=3128, pid=3129, i=1 g ppid=3082, pid=3128, i=0 g ppid=3082, pid=3128, i=1
能夠看到ppid爲3082 pid爲3128的輸出有3次
一樣的,ppid爲3128 pid爲3129的輸出有3次
你將顯然發現,本來i=0時應該只會有兩個「g」被輸出,可是如今竟然詭異地多出了兩個?究竟是哪兩個呢?
我索性標註下這個輸出結果吧~
(1)g ppid=3082, pid=3128, i=0 (5)g ppid=3128, pid=3130, i=1 (2)g ppid=3128, pid=3129, i=0 (6)g ppid=3129, pid=3131, i=1 (3)g ppid=3128, pid=3129, i=0 (7)g ppid=3128, pid=3129, i=1 (4)g ppid=3082, pid=3128, i=0 (8)g ppid=3082, pid=3128, i=1
明顯的,(1)和(4)是「不可能」有兩個的,(2)和(3)也是,如今你知道了吧,i=0時的兩個進程,在下一次fork的時候各自成爲了下一個父進程,在這一次fork時,他們的信息被徹底複製到了子進程,那麼最後一步每個子進程緩衝區裏還存有多餘的「g」(見上圖)
你還能夠用pstree -p | grep fork命令試下
能夠得出一個樹狀的結構:
$ pstree -p | grep fork |-bash(3082)-+-fork(3128)-+-fork(3129)---fork(3131) | | `-fork(3130)
那麼,最後咱們再試試,在printf一個g的後面加上一個換行符(或者fflush)
#include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(void) { int i; for(i=0; i<2; i++){ fork(); printf("g\n"); } wait(NULL); wait(NULL); return 0; }
輸出的結果將是換行的6個「g
緣由就在於,每一次換行,緩衝區的「g」,就會把數據「刷」出緩衝區
(其實或是EOF,或是緩中區滿,或是文件描述符關閉,或是主動flush,或是程序退出)
下次你們在面試的時候若是遇到這個問題,相信均可以迎刃而解了吧 ~~~ (再次感謝酷殼大神,你們對個人見解若是有問題歡迎交流!)