一個關於Linux文件描述符繼承問題的解決過程
來自專欄 如何用編程打發時間最近幾天在調試一個用WebSocket轉發終端的小程序,發現了一個奇怪的問題,就是uv_spawn產生的子進程,會繼承一部分來自父進程的文件描述符,而且子進程有許可權讀寫對應的描述符,關鍵在於一部分,而非全部,這就比較奇怪了,原因可能是什麼呢,我首先想到的是追蹤產生的描述符的句柄。(一開始還有13,我不恢復當時場景了)
下圖是預期結果,也就是我修復問題後的結果。題圖是之前遇到問題的截圖。
執行的命令是ls /proc/self/fd所以能夠看到所有打開的文件句柄。/unknown這種東西要麼是無名管道,要麼就是socket。
無名管道很好排查,列印創建進程過程中的用到的文件句柄——想都不用想一定是openpty的master。
但是如何才能讓子進程不繼承這個描述符呢?由於創建進程用的是libuv的uv_spawn,並不能在exec前執行close函數關閉master,除非修改libuv的代碼,或者採用注入的方法修改libuv的內部執行邏輯,但是為了關閉個文件描述符,動用這種級別的技術實在是得不償失(穩定性之類的會受到很大的影響)。於是只能查找各種相關文檔。
各種關於openpty或者pipe創建子進程的教程大多使用的方法是在fork後關閉描述符,很難找到其他的解決方法(另外誰說必須關閉master才能讀寫slave的?)。
神奇的FD_CLOEXEC
直到我搜索到了FD_CLOEXEC。
它的作用是設置特定文件描述符在執行exec時自動關閉,剛好符合我這裡的需求,於是愉快的加上fcntl (desc, F_SETFD, FD_CLOEXEC);
於是多出來的13描述符就不見了,也就是題圖的效果,然而還剩兩個。這兩個明顯不是我的代碼產生的,然後我覺得可能是libuv的,然後以此為關鍵詞在libuv的代碼庫里搜索,發現libuv的實現里所有的打開的文件描述符都會帶有這個標誌。
後來我用strace也測試了一遍,表明不是libuv的過,那麼就是uWebSocket的問題了。定位了幾個出現打開描述符的函數,在其後加上了設置FD_CLOEXEC(當然,為了健壯性,我先讀取原有的設置,然後再用或運算符加上去),解決了這個問題後,問題自然就都消失了。
這件事告訴我們,查man手冊是很重要的,有時候直接用關鍵字在man手冊里搜索,就能找到需要的東西。。。
推薦閱讀:
※linux下如何刪除文件夾而不刪除文件夾里的內容?
※bash alias怎麼解析傳入的參數的?
※使用PyCharm進行遠程開發和調試
※Linux下有什麼工具可以分析出一個程序的運算時間分布嗎?
※Linux下有哪些遊戲值得推薦?