プロンプトと範囲
liburing は、1 回のリクエスト発行から複数の完了キューエントリ(CQE)を生成できる multishot accept 操作を提供します。このリクエストは、エラー発生時または multishot フラグが存在しない場合に完了通知の生成を停止することがあります。中核となるスキルは非同期システムプログラミングであり、coding に属します。
面接官が評価するポイント
優れた回答では、IORING_CQE_F_MORE、CQE の所有権、送信・完了キューのバックプレッシャー、accept エラー、キャンセル、再登録について説明します。機能を前提とせずにカーネルのサポートをプローブし、バッファやユーザーデータを時期尚早に再利用することを避け、設計を従来のノンブロッキング accept ループと比較します。
最初に確認すべき明確化のための質問
- どのカーネルおよび liburing のバージョンがデプロイされていますか?
- リスニングソケットはワーカー間で共有されていますか、それとも 1 つのリングが所有していますか?
- 予想される接続レート、バーストサイズ、ファイルディスクリプタの上限(budget)はどれくらいですか?
- accept されたソケットはどのようにプロトコルワーカーに渡されますか?
- multishot リクエストが終了したとき、または CQ が満杯になったときは何が起こるべきですか?
- ポータブルな、あるいは io_uring 以外のフォールバックが必要ですか?
30秒の回答フレームワーク
「サポートをプローブし、1 つの multishot accept を発行して、各 CQE を独立して accept されたソケットとして処理します。IORING_CQE_F_MORE を確認し、それが存在しない場合、リクエストはアーム(登録)されておらず、終端結果を処理した後に再発行する必要があります。ループには、有界な CQ 深さ、明示的な EMFILE および一時的エラーの処理、ソケットの引き渡し所有権、およびフォールバック用の accept ループが必要です。CPU、秒あたりの accept 数、テールレイテンシ、ドロップ、CQ オーバーフロー、再登録ギャップをベンチマークします。」
ステップごとの回答
ステップ 1: プローブと設定
multishot accept 操作および必要なフラグについて、リングプローブまたはドキュメントに記載された機能チェックを使用します。バースト率とハンドオフ率からキューサイズを設定し、close-on-exec とノンブロッキング動作を構成し、ダイレクトディスクリプタが管理コストに見合うかどうかを判断します。
ステップ 2: 長期間有効なリクエストの発行
リスニングソケットと世代を識別する安定したユーザーデータを使用して multishot accept リクエストを準備します。1 つの SQE が 1 つの CQE を生成すると想定してはいけません。イベントループの状態内にリクエストのライフサイクルを保持し、終端の完了通知が消費されるまで関連する状態を解放しないようにします。
ステップ 3: すべての完了通知の消費
各 CQE について、結果をチェックして accept されたディスクリプタか負のエラー値かを確認します。成功したソケットの所有権を正確に 1 回だけプロトコルワーカーに転送します。IORING_CQE_F_MORE を検査します。クリアされている場合は、現在の CQE が成功していてもリクエストを非アクティブとしてマークします。
ステップ 4: 再登録とバックプレッシャーの適用
利用可能な CQE をすべてドレイン(排他消費)した後、リクエストが非アクティブであり、システムがさらに作業を受け入れられる場合に再発行します。ハンドオフキューを有界にし、ファイルディスクリプタが逼迫しているときは新しい作業を一時停止または拒否し、ビジーループなしで EMFILE、ENFILE、および一時的なネットワークエラーを処理します。
ステップ 5: シャットダウンとベンチマーク
シャットダウン時にリクエストをキャンセルまたはクローズし、CQE をドレインして、所有者のいない accept されたソケットをクローズします。同一のコア数、バックログ、接続構成、ワーカー容量の下で multishot と従来の accept を比較します。システムコール数だけでなく、再登録のギャップやドロップ/拒否された接続を測定します。
模範回答
「multishot accept は発行オーバーヘッドを削減しますが、有限のリクエストライフタイムを持つ CQE のストリームです。私はサポートをプローブし、安定したユーザーデータを発行し、accept された各ディスクリプタを 1 回消費し、すべての完了通知で IORING_CQE_F_MORE を検査します。フラグが消えたらリクエストを非アクティブとしてマークし、終端結果を処理した後に再登録します。キューの深さ、ハンドオフのバックプレッシャー、EMFILE 処理、シャットダウン時のドレイン、および従来の accept フォールバックは、正確性を保つための構成要素です。ベンチマークには、再登録ギャップ、ドロップ、テールレイテンシ、および CPU を含める必要があります。」
よくある間違い
- リクエストが永続的であると仮定する → エラー後または MORE がない場合に完了通知が停止する → 明示的に再登録する。
- 1 つの CQE を全体の結果として扱う → 以降に accept されたソケットを見落とす → すべての CQE をドレインする。
- ユーザーデータを早期に解放する → 後続の完了通知が無効な状態を使用する → 終端の完了通知まで状態を保持する。
- 負の結果を無視する → ループが空回りするかリソース枯渇を隠蔽する → エラーを分類してバックオフする。
- 無制限のハンドオフキュー → accept されたディスクリプタがプロセスを枯渇させる → ディスクリプタとキューの予算を適用する。
- システムコールのみをベンチマークする → 再登録ギャップやドロップが隠れたままになる → エンドツーエンドの接続結果を測定する。
フォローアップの質問
フォローアップ 1: IORING_CQE_F_MORE は何を意味しますか?
multishot リクエストがさらに多くの CQE を生成することが期待されていることを示します。存在しない場合、リクエストは終了しており、アプリケーションは別の完了通知が届くと想定してはなりません。
フォローアップ 2: 成功した CQE が終端のものになることはありますか?
はい。CQE に有効な accept されたディスクリプタが含まれていながら、MORE フラグがない場合があります。ソケットを処理してから、リクエストを再登録してください。
フォローアップ 3: ディスクリプタの枯渇をどのように防ぎますか?
プロトコルのハンドオフを有界にし、RLIMIT_NOFILE を監視し、EMFILE および ENFILE を処理し、容量が回復するまで accept を停止するか負荷を軽減(shed load)します。
フォローアップ 4: なぜフォールバックを維持するのですか?
カーネル、liburing、コンテナ、またはポリシーの制約により、io_uring が使用できない場合があります。ノンブロッキング accept ループは可用性を維持し、パフォーマンス比較のための正確性のベースラインを提供します。