1. 程式人生 > >使用FD_CLOEXEC實現close-on-exec,關閉子程序無用檔案描述符

使用FD_CLOEXEC實現close-on-exec,關閉子程序無用檔案描述符

通過fcntl設定FD_CLOEXEC標誌有什麼用?  
  1.  close on exec, not on-fork, 意為如果對描述符設定了FD_CLOEXEC,使用execl執行的程式裡,此描述符被關閉,不能再使用它,但是在使用fork呼叫的子程序中,此描述符並不關閉,仍可使用。  
通過fcntl設定FD_CLOEXEC標誌有什麼用?
 close on exec, not on-fork, 意為如果對描述符設定了FD_CLOEXEC,使用execl執行的程式裡,此描述符被關閉,不能再使用它,但是在使用fork呼叫的子程序中,此描述符並不關閉,仍可使用。

我們經常會碰到需要fork子程序的情況,而且子程序很可能會繼續

exec新的程式。這就不得不提到子程序中無用檔案描述符的問題!

fork函式的使用本不是這裡討論的話題,但必須提一下的是:子程序以寫時複製(COW,Copy-On-Write)方式獲得父程序的資料空間、堆和棧副本,這其中也包括檔案描述符。剛剛fork成功時,父子程序中相同的檔案描述符指向系統檔案表中的同一項(這也意味著他們共享同一檔案偏移量)。

接著,一般我們會呼叫exec執行另一個程式,此時會用全新的程式替換子程序的正文,資料,堆和棧等。預設子程序的檔案描述符複製到exec後的程序中,這樣的話,如果父程序關閉(killall *)之後,也無法呼叫驅動程式中的release函式來釋放檔案struct file 結構,當子程序結束(killall *)時,才會呼叫realse函式釋放open開啟的檔案。所以通常我們會fork子程序後在子程序中直接執行close關掉對子程序無用的檔案描述符,然後再執行exec。

但是在複雜系統中,有時我們fork子程序時已經不知道打開了多少個檔案描述符(包括socket控制代碼等),這此時進行逐一清理確實有很大難度。我們期望的是能在fork子程序前開啟某個檔案控制代碼時就指定好:“這個控制代碼我在fork子程序後執行exec時就關閉”。其實時有這樣的方法的:即所謂的 close-on-exec。

close-on-exec的實現只需要呼叫系統的fcntl就能實現,很簡單幾句程式碼就能實現:

     int fd=open("foo.txt",O_RDONLY);
     int flags = fcntl(fd, F_GETFD);
     flags |= FD_CLOEXEC;
     fcntl(fd, F_SETFD, flags);

這樣,當fork子程序後,仍然可以使用fd。但執行exec後系統就會欄位關閉子程序中的fd了,如果不設定該close_on_exec標誌位,子程序的fd將複製到exec之後的程序中。