題目與範圍
程序退出後數字 PID 可能重用,延遲訊號或狀態檢查會命中錯誤程序。Linux 提供 pidfdopen() 取得指向程序的檔案描述元,pidfdsend_signal() 透過它傳送訊號。核心能力是系統程式設計與生命週期正確性,因此分類為 coding。
面試官考察點
應解釋描述元身分、poll/epoll 就緒、waitid 整合、close-on-exec、權限和清理。區分 pidfd 與無限權限的程序控制代號,處理 fork/exec 競態,並定義退出、重啟、逾時和監管器崩潰行為。還要說明核心特性偵測和經過測試的回退。
先釐清的問題
- 監管器需要支援哪些核心版本和命名空間?
- 它自己啟動子程序,還是附著既有程序?
- 需要觀察退出狀態、傳送訊號,還是兩者都要?
- 工作程序會 fork 後代嗎,後代由誰清理?
- 逾時、重啟和監管器崩潰保證是什麼?
- 不支援 pidfd 的核心是否需要回退?
30 秒答題框架
「為每個受管程序取得 pidfd,儲存描述元而非只儲存數字 PID。把描述元加入 poll 或 epoll,就緒後用規範等待操作收集狀態,用 pidfdsendsignal() 處理逾時和關閉。設定 close-on-exec,在每條終止路徑關閉描述元,把就緒與狀態收集納入同一狀態機。測試 PID 重用、快速退出、權限錯誤、後代程序、核心偵測和監管器重啟。」
分步作答
步驟 1:取得並擁有描述元
啟動或定位程序後,在支援時呼叫 pidfd_open(),把描述元放進所有者表。設定 close-on-exec,數字 PID 只用於日誌。沒有明確所有權轉移時,不把描述元交給無關工作器。
步驟 2:觀察生命週期事件
把 pidfd 註冊到 poll 或 epoll。就緒表示引用的程序已退出;用適當等待操作收集狀態,再關閉描述元。不能依賴陳舊 /proc 路徑或 PID 整數判斷存活。
步驟 3:向正確程序發訊號
用 pidfdsendsignal() 執行優雅終止和升級終止,顯式處理權限和命名空間錯誤。pidfd 能指向原程序,即使數字 PID 後來重用,仍不能繞過授權檢查。
步驟 4:建模重啟與後代
把工作器狀態定義為啟動、運行、停止、退出或失敗。重啟時產生新的 pidfd 和代次記錄。明確後代屬於程序組、cgroup 還是獨立所有域,不能假設殺父程序會清理所有子程序。
步驟 5:測試競態與可移植邊界
壓測快速退出和 PID 重用、退出並發訊號、描述元耗盡、監管器崩潰、命名空間變化和不支援的核心。與直接子程序的 waitpid 等受限回退比較,並記錄回退無法提供的保證。
參考答案
「缺陷來自把可重用整數當成程序身分。我會為每個工作器儲存 pidfd,加入事件迴圈,就緒後收集退出狀態,並用 pidfdsendsignal() 執行優雅停止和升級終止。描述元所有權、close-on-exec、權限和後代清理成為顯式狀態機規則。每次重啟建立新的代次和 pidfd。測試必須強制快速退出與 PID 重用,涵蓋命名空間和核心支援,並記錄直接子程序回退較弱的保證。」
常見錯誤
- 只儲存數字 PID → 延遲訊號與 PID 重用競態 → 保留 pidfd。
- 輪詢
/proc判斷存活 → 觀察結果會過期 → 使用 pidfd 就緒和狀態收集。 - 認為 pidfd 繞過權限 → 訊號仍需授權 → 處理權限錯誤。
- exec 時洩漏描述元 → 無關程式繼承生命週期控制代號 → 設定 close-on-exec。
- 只殺父程序 → 後代成為孤兒 → 定義程序組或 cgroup 所有權。
- 把就緒當成完整狀態 → 退出碼處理不完整 → 收集並儲存等待狀態。
追問
追問 1:pidfd 能阻止 PID 重用嗎?
對於接受 pidfd 的操作,它提供穩定的描述元引用。數字 PID 可以重用,但透過描述元執行的操作仍指向原程序。
追問 2:pidfd 能和 epoll 一起用嗎?
可以。pidfd 會在程序退出時變為可輪詢,因此能和管道、計時器及控制 socket 一起進入監管器事件迴圈。
追問 3:核心不支援 pidfd 怎麼辦?
啟動時偵測能力,對直接子程序使用文件化回退,並公開較弱的競態和可觀測性保證,不能假裝等價。
追問 4:監管器重啟後怎麼辦?
持久化足夠的所有權元資料以重新發現或有意放棄工作器,再重建監控狀態和 pidfd。不能只信任持久化數字 PID。