プロンプトとスコープ
プロセスの終了後に数値 PID が再利用される可能性があるため、シグナルの送信や状態確認が遅延すると誤ったプロセスを対象にしてしまうことがあります。Linux はプロセスを参照するファイル記述子として pidfd_open() を、そのプロセスにシグナルを送信するために pidfd_send_signal() を提供しています。中核となるスキルはシステムプログラミングとライフサイクルの正確性であるため、これは coding の問題です。
面接官が評価するポイント
優れた回答では、記述子の同一性、poll または epoll のレディ状態、waitid の統合、close-on-exec、パーミッション、クリーンアップについて説明します。pidfd と無制限のアクセスを許可するプロセスハンドルを区別し、fork/exec の競合を処理し、終了、再起動、タイムアウト、スーパーバイザのクラッシュに対する動作を定義します。また、カーネルバージョンの機能検出とテスト済みのフォールバックについても言及します。
最初に明確にすべき質問
- スーパーバイザはどのカーネルバージョンと名前空間をサポートする必要がありますか?
- 子プロセスを自身で起動しますか、それとも既存のプロセスにアタッチしますか?
- 終了ステータスの監視、シグナルの送信、あるいはその両方を行う必要がありますか?
- ワーカーが子孫プロセスを fork する可能性はありますか?また、そのクリーンアップの所有者は誰ですか?
- タイムアウト、再起動、スーパーバイザのクラッシュに関する保証はどのようなものですか?
- pidfd をサポートしていないカーネルでのフォールバックは必要ですか?
30秒の回答フレームワーク
「管理対象の各プロセスに対して pidfd を取得し、数値 PID だけでなく記述子も保存します。記述子を poll または epoll に追加し、文書化された wait 操作を使用してステータスを収集し、タイムアウトやシャットダウンには pidfd_send_signal() を呼び出します。close-on-exec を設定し、すべての終了パスで記述子をクローズして、レディ状態とステータス収集を単一のライフサイクルステートマシンとして扱います。PID の再利用、急激な終了、パーミッションエラー、fork された子プロセス、カーネル機能の検出、スーパーバイザの再起動をテストします。」
ステップごとの回答
ステップ 1:記述子の取得と所有
プロセスの起動後または特定後に、サポートされている環境で pidfd_open() を呼び出し、所有者テーブルに記述子を記録します。close-on-exec とマークし、数値 PID はログのためだけに保持します。明示的な所有権の移転なしに、無関係なワーカーに記述子を渡してはなりません。
ステップ 2:ライフサイクルイベントの監視
pidfd を poll または epoll に登録します。レディ状態は参照されているプロセスが終了したことを示します。適切な wait 操作でそのステータスを収集し、記述子をクローズします。古くなった /proc のパスや PID の整数値から生存状態を推測してはなりません。
ステップ 3:適切なプロセスへのシグナル送信
グレースフルな終了およびエスカレーションには pidfd_send_signal() を使用します。パーミッションおよび名前空間のエラーは明示的に処理します。pidfd は、その数値 PID が後に再利用された場合でも参照されたプロセスを識別しますが、認可チェックを代替するものではありません。
ステップ 4:再起動と子孫プロセスのモデル化
各ワーカーを starting、running、stopping、exited、failed のいずれかとして表現します。再起動時には、新しい pidfd と世代レコードを作成します。子孫プロセスが同じプロセスグループ、cgroup、または別個の所有ドメインにあるかどうかを決定します。親プロセスにシグナルを送信すればすべての子プロセスがクリーンアップされると決して仮定してはなりません。
ステップ 5:競合と移植性の境界テスト
急激な終了と PID の再利用、終了と競合するシグナル、記述子の枯渇、スーパーバイザのクラッシュ、名前空間の変更、非サポートのカーネルに対してストレステストを実施します。直接の子プロセスに対する waitpid などの慎重に制約されたフォールバックと比較し、そのフォールバックでは提供できない保証を記録します。
模範回答
「この不具合は、再利用可能な整数をプロセス識別子として使用していることに起因します。ワーカーごとに pidfd を保存し、イベントループに登録し、レディ状態になった後に終了ステータスを収集し、グレースフルな停止とエスカレーションのために pidfd_send_signal() を介してシグナルを送信します。記述子の所有権、close-on-exec、パーミッション、および子孫プロセスのクリーンアップは、明示的なステートマシンルールになります。再起動ごとに新しい世代と pidfd が付与されます。テストでは、急激な終了と PID の再利用を強制し、名前空間とカーネルのサポートをカバーし、直接の子プロセス向けフォールバックのより弱い保証を文書化する必要があります。」
よくある間違い
- 数値 PID のみを保持する → 遅延シグナルが PID の再利用と競合する → pidfd を保持する。
- 生存確認のために
/procをポーリングする → 観測結果が古くなる → pidfd のレディ状態とステータス収集を使用する。 - pidfd がパーミッションをバイパスすると仮定する → シグナルには依然として認可が必要 → パーミッションエラーを処理する。
- exec をまたいで記述子をリークする → 無関係なプログラムがライフサイクルハンドルを継承してしまう → close-on-exec を設定する。
- 親プロセスのみを kill する → 子孫プロセスが孤立したままになる → グループまたは cgroup の所有権を定義する。
- レディ状態を完全なステータスとして扱う → 終了コードの処理が不完全になる → wait ステータスを収集して永続化する。
フォローアップの質問
フォローアップ 1:pidfd は PID の再利用を防ぎますか?
pidfd を受け入れる操作に対して、プロセスへの安定した記述子参照を提供します。数値 PID は再利用される可能性がありますが、記述子を介した操作は元のプロセスを参照し続けます。
フォローアップ 2:pidfd を epoll と一緒に使用できますか?
はい。pidfd はプロセスの終了に対してポーリング可能であるため、パイプ、タイマー、制御ソケットと並んでスーパーバイザのイベントループに参加できます。
フォローアップ 3:カーネルが pidfd をサポートしていない場合はどうなりますか?
起動時に機能を検出し、直接の子プロセスに対して文書化されたフォールバックを使用します。その際、同等であるかのように装うのではなく、競合や監視に関する保証が弱くなることを明示します。
フォローアップ 4:スーパーバイザが再起動した後は何が起こりますか?
ワーカーを再検出するか意図的に破棄するのに十分な所有権メタデータを永続化し、監視状態と pidfd を再作成します。永続化された数値 PID 単体だけを決して信頼してはなりません。