標籤:linux 連接埠佔用 sock_cloexec close-on-exec fd_cloexec
轉載請註明出處:簾卷西風的專欄(http://blog.csdn.net/ljxfblog)
前幾天寫了一篇部落格,講述了連接埠佔用情況的查看和解決。
關於linux系統連接埠查看和佔用的解決方案
大部分這種問題都能夠解決,在文章的最後,提到了一種特殊情況,就是父子進程中的連接埠佔用情況。父進程監聽一個連接埠後,fork出一個子進程,然後kill掉父進程,再重啟父進程,這個時候提示連接埠佔用,用netstat查看,子進程佔用了父進程監聽的連接埠。
原理其實很簡單,子進程在fork出來的時候,使用了寫時複製(COW,Copy-On-Write)方式獲得父進程的資料空間、 堆和棧副本,這其中也包括檔案描述符。剛剛fork成功時,父子進程中相同的檔案描述符指向系統檔案表中的同一項(這也意味著他們共用同一檔案位移量)。這其中當然也包含父進程建立的socket。
接著,一般我們會調用exec執行另一個程式,此時會用全新的程式替換子進程的本文,資料,堆和棧等。此時儲存檔案描述符的變數當然也不存在了,我們就無法關閉無用的檔案描述符了。所以通常我們會fork子進程後在子進程中直接執行close關掉無用的檔案描述符,然後再執行exec。
但是在複雜系統中,有時我們fork子進程時已經不知道開啟了多少個檔案描述符(包括socket控制代碼等),這此時進行逐一清理確實有很大難度。我們期望的是能在fork子進程前開啟某個檔案控制代碼時就指定好:“這個控制代碼我在fork子進程後執行exec時就關閉”。其即時有這樣的方法的:即所謂 的 close-on-exec。
回到我們的應用情境中來,只要我們在建立socket的時候加上SOCK_CLOEXEC標誌,就能夠達到我們要求的效果,在fork子進程中執行exec的時候,會清理掉父進程建立的socket。
#ifdef WIN32SOCKET ss = ::socket(PF_INET, SOCK_STREAM, 0);#elseSOCKET ss = ::socket(PF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);#endif
當然,其他的檔案描述符也有類似的功能,例如檔案,可以在開啟的時候使用O_CLOEXEC標識(linux 2.6.23才開始支援此標記),達到和上面一樣的效果。或者使用系統的fcntl函數設定FD_CLOEXEC即可。
//方案Aint fd = open(“foo.txt”,O_RDONLY);int flags = fcntl(fd, F_GETFD);flags |= FD_CLOEXEC;fcntl(fd, F_SETFD, flags);//方案B,linux 2.6.23後支援int fd = open(“foo.txt”,O_RDONLY | O_CLOEXEC);
好了,現在我們終於可以完美的解決連接埠佔用這個令人煩惱的問題了。
關於linux進程間的close-on-exec機制