fork和vfork都是創建進程,exit和_exit都是退出進程,但之間也有些細微的區別,並且很讓人迷惑。一般情況下用fork創建的子進程用exit結束,而用vfork創建的進程則用_exit。那他們具體的區別在哪兒了,爲什麼這麼做!
預備知識:程序的存儲空間佈局,系統的緩衝區
c程序在內存中從高地址到低地址依次爲:
命令行參數和環境變量:存儲命令行的各個參數和系統的環境變量
棧段:局部變量(函數內部定義使用的變量)和函數調用存放的位置,此塊由操作系統處理
堆段:通常在堆裏面進行動態存儲分配,malloc就是分配堆裏面的空間,通常由程序員控制
數據段:存儲一些初始化數據和非初始化數據
正文段:cpu執行的機器碼
系統的緩衝區:
操作系統爲了提高文件都寫效率,都使用的緩存機制,當我們寫入一個文件時,很有可能寫入了緩衝區而沒有真真意義上的寫入硬盤,緩存分爲三個級別:
全緩衝:當緩衝空間(4096B或其它)寫滿後,一次性寫入硬盤,文件緩存通常如此
行緩衝:遇到輸入輸出遇到換行符,執行寫入操作,命令行界面就是使用行緩衝
無緩衝:這個就不用解釋了
fork與vfork
執行fork創建子進程時,子進程將拷貝父進程的數據段、堆段和棧段,此時父子進程的數據一致當互相獨立,各不影響,子進程對數據的處理不影響父進程。(其實當父子進程都不對三個段進行寫操作時,父子進程仍然共享數據段、堆段和棧段,當有任何一方有寫操作時,才產生副本,這種方式成爲寫是複製)
vfork與fork區別有兩點:1、vfork創建的子進程與父進程共享數據段,2、vfork創建的子進程優先於父進程執行,當子進程執行時,父進程進入阻塞狀態。
所以vfork產生的子進程對數據的修改一定可以影響到父進程的數據
exit與_exit
exit:當執行exit時,終止處理程序,執行標準的I/O清楚操作(將緩存中文件寫入),調用atexit,調用_exit。由此可見,exit是加強版的_exit.
_exit:通知內核,進程結束
測試exit和_exit
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- int main(void)
- {
- printf("the first line\n");
- printf("the second line\n");
- exit(0);
- }
- eno@eno-ThinkPad-T420:~$ vim exit.c
- eno@eno-ThinkPad-T420:~$ gcc exit.c -o exit
- eno@eno-ThinkPad-T420:~$ ./exit
- the first line
- the second line
- eno@eno-ThinkPad-T420:~$
結果與預期相同,改爲_exit(0)
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- int main(void)
- {
- printf("the first line\n");
- printf("the second line\n");
- _exit(0);
- }
- eno@eno-ThinkPad-T420:~$ ./_exit
- the first line
- the second line
- eno@eno-ThinkPad-T420:~$
結果怎麼還是一樣的,有的系統可能看到是沒有輸出,這都是正常的,每個系統最輸出的緩存機制可能不一致,通常爲行緩存,把他強制設置成全緩存看看:
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- int main(void)
- {
- char buf[4096];
- //設置成全緩存
- setvbuf(stdout, buf, _IOFBF, 4096);
- printf("the frist line\n");
- printf("the second line\n");
- _exit(0);
- }
ok,結果與預期相同,緩衝區的數據全部沒掉了
- eno@eno-ThinkPad-T420:~$ gcc _exit.c -o _exit
- eno@eno-ThinkPad-T420:~$ ./_exit
- eno@eno-ThinkPad-T420:~$
這麼一比劃,就基本上明白了爲什麼fork產生的子進程要用exit而不用_exit了,如果緩衝區的東西都丟了,子進程所產生的數據就不完整,這對於程序使用者來說是不可接受的。那爲什麼vfork一般用_exit了,難道不怕丟數據麼?用vfork的子程序通常用_exit結束,而父進程通常用exit結束,這樣做數據就不會丟失,因爲他們共享一個數據段,父進程結束會處理所有打開的數據流,將其寫入。萬一子程序也用exit結束,那就比較麻煩了,試想一下父子進程都把同一份數據寫入,那個文件的價值有在哪裏。