具代表性的面試主題

程式設計面試:如何安全地使用 io_uring multishot accept?

程式題困難
Offer.cc 編輯團隊發佈 更新

題幹

一個 Linux TCP 閘道每個連線提交一次 accept,CPU 消耗明顯。你如何評估 io_uring multishot accept,安全處理完成事件,並避免請求結束時遺失連線?

題目與範圍

liburing 的 multishot accept 能讓一次提交產生多個完成佇列事件(CQE)。請求可能在錯誤後停止,或在完成事件沒有 multishot 標記時終止。核心能力是非同步系統程式設計,因此分類為 coding

面試官考察點

應解釋 IORING_CQE_F_MORE、CQE 所有權、提交與完成佇列背壓、accept 錯誤、取消和重新佈置。先探測核心支援,不能過早重用緩衝區或使用者資料,並與傳統非阻塞 accept 迴圈比較。

先釐清的問題

  • 部署的核心和 liburing 版本是什麼?
  • 監聽 socket 由一個 ring 擁有,還是多個工作器共享?
  • 連線速率、突發量和檔案描述元預算是多少?
  • 接受的 socket 如何交給協議工作器?
  • multishot 請求終止或 CQ 滿時怎麼辦?
  • 是否需要不依賴 io_uring 的回退?

30 秒答題框架

「先探測支援,提交一個 multishot accept,把每個 CQE 當成獨立接受的 socket 處理。檢查 IORING_CQE_F_MORE;標誌缺失時請求已不再佈置,處理終止結果後必須重新提交。迴圈需要有界 CQ、明確處理 EMFILE 與瞬時錯誤、socket 交接所有權和普通 accept 回退。基準比較 CPU、每秒接受數、尾延遲、丟棄、CQ 溢出和重新佈置間隙。」

分步作答

步驟 1:探測並配置

使用 ring 探針或文件化能力檢查,確認 multishot accept 和所需標誌。依突發量與交接速率配置佇列大小,設定 close-on-exec 和非阻塞行為,並權衡直接描述元的管理成本。

步驟 2:提交長生命週期請求

準備穩定的使用者資料,標識監聽 socket 和代次。不能假設一個 SQE 只產生一個 CQE。請求生命週期屬於事件迴圈狀態,關聯狀態必須等到終止完成事件消費後才能釋放。

步驟 3:消費每個完成事件

逐個檢查 CQE:結果可能是接受的描述元,也可能是負錯誤。成功 socket 只向協議工作器轉移一次所有權。每次檢查 IORING_CQE_F_MORE;即使當前 CQE 成功而標誌缺失,也要把請求標為非活動。

步驟 4:重新佈置並施加背壓

清空可用 CQE 後,若請求非活動且系統仍能接收工作,就重新提交。限制交接佇列,檔案描述元壓力升高時暫停或拒絕新工作,並處理 EMFILEENFILE 和瞬時網路錯誤,避免忙等。

步驟 5:關閉與基準

關閉時取消或關閉請求,排空 CQE,並關閉未交接的接受 socket。用相同 CPU、backlog、連線組合和工作器容量比較 multishot 與傳統 accept。除了系統呼叫數,還要測量重新佈置間隙以及遺失或拒絕的連線。

參考答案

「Multishot accept 能降低提交成本,但它是有生命週期的 CQE 流。先探測支援,提交穩定使用者資料,每個接受描述元只處理一次,並在每個完成事件檢查 IORING_CQE_F_MORE。標誌消失後,把請求標為非活動,處理終止結果再重新佈置。佇列深度、交接背壓、EMFILE、關閉排空和普通 accept 回退都是正確性的一部分。基準必須包括重新佈置間隙、丟棄、尾延遲和 CPU。」

常見錯誤

  • 認為請求永久有效 → 錯誤或缺少 MORE 後完成會停止 → 顯式重新佈置。
  • 把一個 CQE 當成全部結果 → 後續連線被漏掉 → 排空所有 CQE。
  • 過早釋放使用者資料 → 後續完成事件存取無效狀態 → 到終止完成後再釋放。
  • 忽略負結果 → 迴圈忙等或隱藏資源耗盡 → 分類錯誤並退避。
  • 交接佇列無界 → 接受描述元耗盡程序 → 設定描述元和佇列預算。
  • 只測系統呼叫 → 重新佈置間隙和遺失連線不可見 → 測量端到端連線結果。

追問

追問 1:IORING_CQE_F_MORE 表示什麼?

它表示 multishot 請求預計還會產生更多 CQE。標誌缺失時請求已結束,應用不能假設會有下一次完成事件。

追問 2:成功 CQE 可以是終止事件嗎?

可以。CQE 可能帶有效接受描述元卻沒有 MORE。先處理 socket,再重新佈置請求。

追問 3:如何避免描述元耗盡?

限制協議交接,監控 RLIMIT_NOFILE,處理 EMFILEENFILE,容量恢復前暫停接受或削峰。

追問 4:為什麼保留回退?

核心、liburing、容器或策略限制可能阻止 io_uring。非阻塞 accept 保持可用性,也提供效能比較的正確性基線。

公開來源

同類題目

相關面試工具

用 Screenshot 處理演算法題

截圖題目後,依序看約束、解法、程式碼、邊界條件和複雜度。

查看工具