在子進程中調用exit()函數對標準I/O流的影響

我們知道exit()函數是用來終止程序用的,它將調用exit系統調用,將程序狀態作爲參數返回給內核。但是之於_exit()和_Exit(),exit()的不同之處在於,exit()在調用exit系統調用之前,它將做一些最後的處理,包括兩個部分:調用由atexit()註冊的終止處理程序;關閉打開的流。這裏我們要討論的是第二點。


fork()

我們都知道,由fork()創建的子進程是父進程的副本,即使是採用了cow技術,在修改時也還是會創建副本再修改。因此,在父進程中打開的流也會複製到子進程中,如下面的代碼。




上面的代碼可以看出,在fork()之前我們有一條printf輸出語句,輸出“before fork()!”字符串,然後父進程創建了一個子進程之後,在子進程中什麼都沒有做,只是調用了exit()函數來關閉流。我們分析一下過程,從父進程開始執行,打開了一個流,輸出了“before fork()”字符串,然後緊接着fork()了一個子進程,然後是先執行哪個進程我們並不知曉,但是從運行結果來看貌似是printf語句執行了兩次。首先有一次是父進程執行的printf語句,而另外一次呢?這是因爲,在父進程調用fork()時,之前輸出的字符串仍然還在緩衝區中,然後fork()函數將父進程的數據空間複製到子進程中,然後當進程終止時,將調用exit()函數,並將清除緩衝區中的內容。將如這裏將exit()函數改成_exit(),情況又不同了,如下




我們可以看到,什麼都沒有輸出。因爲在父進程的標準IO流輸出緩衝區中有字符串,然後fork子進程後在子進程中也有一個該字符串的副本,但是由於兩個進程都是調用_exit函數來終止進程,而這個函數在終止進程的時候並不刷新IO緩衝區,因此會導致父、子進程的輸出均未被刷新到標準設備上。



vfork()

我們知道,vfork()創建的子進程在調用exec或者exit函數之前將與父進程共用虛擬地址空間,如果我們在子進程調用這兩個函數之前關閉就關閉了打開流,就等於關閉了父進程的打開流,另外,由於vfork總是使得子進程先執行,這會導致父進程無法輸出流。如下代碼可以表示之。




從上面的代碼可以看出,我們在父、子進程中都調用exit函數來刷新緩衝區,但是由於子進程在exit之前都是與父進程共用地址空間,而且vfork總是讓子進程先執行,因此在子進程中調用exit函數,先刷新了緩衝區,即刷新了兩個進程的共有緩衝區,斷開了IO流,然後終止自己,然後父進程被調度執行,然後父進程在執行exit的時候,已經不存在打開着的流了,因此在試圖刷新IO緩衝區時沒有任何操作,因此在父進程中沒有執行任何語句了。


因此我們發現,fork的父子進程總是獨立的,而vfork的子進程總是用來exec一個新程序的,即後者用在用戶已經知道創建子進程的目的是用來執行新程序的情況下。


另外,根據vfork的性質,我們很容易想到進程中的線程,同一進程中的線程總是共用進程的地址空間的,其實線程也是通過vfork來實現的。
發佈了68 篇原創文章 · 獲贊 42 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章