ソフトウェア

プロセスが終了しても入出力は行われるのか?プロセスが終了するとio_uringはどうなるのか?


一般的なイメージではOSのI/O操作はプロセスが終了すると停止するものと受け止められているのではないでしょうか。しかしながら、LinuxカーネルにおけるI/O操作のライフサイクルは必ずしもプロセスのライフサイクルと一致しないことがあります。特にLinuxカーネル5.1で導入されたio_uringを使用した場合、プロセスが終了した後でもI/O操作がカーネル内で継続しストレージに到達する可能性がデータベースプラットフォーム開発者のエフゲニー・イワノフ氏により報告されています。

Is There I/O After Death? What Happens to io_uring When a Process Dies | by Evgenii Ivanov | Sep, 2026 | YDB.tech blog
https://blog.ydb.tech/is-there-i-o-after-death-what-happens-to-io-uring-when-a-process-dies-92c65354873f?postPublishedType=repub

従来のLinuxネイティブAIOSQPOLLモードのio_uringを用いてストレージへの書き込みを行う場合は、プロセスに対してwaitpid()がコールされるとプロセスが関連するI/O操作も終了し、書き込み完了を待ってwaitpid()が値を返します。しかしながら、通常のio_uringではプロセス終了後も以前に送信された書き込みがカーネル内で保持され、後からデバイスに到達することがあります。つまり、waitpid()は単にプロセスの終了を通知するだけであり、I/O操作の完了を保証するものではないということです。両者の挙動の差は通常は微々たるものといえますが、フェイルファスト型、つまり異常やエラーを検知した際に即座に処理を中断・停止させる設計方針に基づくアプリケーションの場合は重要な違いとなります。アプリケーションプロセスのI/O操作がまだ処理中の段階で後継プロセスによるリカバリ・ストレージ検査・書き込みが開始されてしまう可能性があるからです。

I/O処理の挙動を実験するために、イワノフ氏は親子プロセスからなる簡単なテストプログラムを作成しました。子プロセスは書き込みプロセスとして、動作中に1024個・4KiBのブロックを循環して「子プロセスのPID」と「書き込み回数」を上書きし続けます。親プロセスは以下の処理を行います。

・1. 子プロセスにSIGKILLを送信する
・2. waitpid()で子プロセスの終了を待つ
・3. 子プロセスの書き込み対象である1024個のブロックをすべて読む
・4. しばらく後にもう一度1024個のブロックをすべて読む
・5. ブロックごとに内容を比較する

もし2つのスナップショットで差分が発生していたら、waitpid()が戻ってきた後に終了したはずの子プロセスがストレージへの書き込みを行ったことになります。

・実験1:そのまま実行
まずはアイドル状態のNVMeデバイス上で実験します。

SIGKILL at +1000.061 ms
waitpid took 7.514 ms

first snapshot:  1024/1024 blocks from writer
second snapshot: 1024/1024 blocks from writer

all 1024 positions unchanged

一見すると2つのスナップショットで差分はありませんが、だからといって子プロセスの終了後にストレージへの書き込みが行われていないことを示すわけではありません。アイドル状態のNVMeは単純に速すぎるため、子プロセス終了時に未処理の書き込みが親が最初のスナップショットを実行する前、もしくは実行中に完了している可能性があります。

・実験2:dm-delayによる書き込み遅延
Linuxにはdm-delayという便利なデバイスマッパーが存在し、ストレージのレスポンスが遅い状態を再現することができるので、3秒間の書き込み遅延を設定しました。子プロセスは遅延マッピングによりゆっくり書き込む一方で、親プロセスはダイレクトに読み込むことができます。

SIGKILL at +1000.064 ms
waitpid took 2.951 ms

first snapshot:
    0/1024 blocks from current writer

second snapshot:
    1024/1024 blocks from current writer

1024/1024 positions changed

I/O from the grave dettected

子プロセスが停止後waitpid()約3ミリ秒で戻り、ほぼ3秒後に書き込みが行われました。つまりwaitpid()完了後もI/Oが進行していることを示しています。

・実験3:fioによるランダムリード
dm-delayによる人工的な遅延を使用せずネイティブなNVMeキューで同様の動作を確認するため、fioによるランダムリードワークロードを行わせました。

fio --name=grave_readload \
    --filename=/dev/nvme2n1p2 \
    --readonly \
    --rw=randread \
    --bs=4096 \
    --direct=1 \
    --ioengine=io_uring \
    --iodepth=2048 \
    --numjobs=32

書き込みプロセス実行中にシグナルキルしたところ、やはり以前の書き込みがプロセス終了後にストレージまで到達することが観測され、io_uringの非同期I/Oがプロセス終了後も継続しうることを示唆していました。

SIGKILL at +1000.059 ms
waitpid took 8.028 ms

first snapshot took 252.735 ms
second snapshot took 177.544 ms

627/1024 positions changed
I/O from the grave dettected

・対策
プロセス終了後のI/O操作による影響を排除する簡単な対策は、書き込み側プロセスがデバイスを開く際に排他ロックをかける「バリア機構」を用意することであるとイワノフ氏は述べています。


書き込み側プロセスはopen()を使用してデバイスを開き、ファイルディスクリプタを使用してflock()による排他ロックをかけます。

int fd = open(path, O_RDWR | O_DIRECT);
flock(fd, LOCK_EX);

書き込み側プロセスを終了させた後、後継プロセスは同じロックを取得する必要があります。

int fd = open(path, O_RDWR | O_DIRECT);

for (;;) {
    if (flock(fd, LOCK_EX | LOCK_NB) == 0) {
        break; // ロック取得成功
    }
    if (errno != EWOULDBLOCK && errno != EAGAIN) {
        perror("flock");
        abort();
    }
    usleep(1000); // 1ms後に再びロック取得を試す
}

プロセス終了後も未完了のI/Oリクエストがファイルディスクリプタへの参照を保持するため、後続プロセスはロックを取得できません。よって、上記の実装を行えばI/O完了まで待機させる実質的なバリアとして機能するというわけです。

以下の表はLinux 6.6.79でのI/Oリクエストとバリア機構に関する挙動をまとめたものです。


書き込み方法waitpid()が未処理の書き込みをクリアするか?waitpid()後に後続プロセスがすぐロックできるか?
通常のio_uringしない直後はEWOULDBLOCKエラー、後ほど成功
SQPOLLモードのio_uringする直後に成功
LinuxネイティブAIOする直後に成功


通常のio_uringでは、書き込みプロセスが終了した後にストレージのロック取得結果が変化する様子を確認することができました。一方、SQPOLLモードのio_uringおよびLinuxネイティブAIOでは、書き込みがすべて完了するまでwaitpid()は返りませんでした。

結論として、LinuxカーネルにおいてプロセスのライフサイクルとI/Oのライフサイクルは常に一致するわけではないことが確認できました。特に通常のio_uringではwaitpid()がプロセスの終了を通知してもI/Oが未完了である可能性があります。リカバリープロトコルなどでwaitpid()を終了プロセスによるI/O操作のバリアとして利用する際に通常のio_uringが示す挙動は問題となり得るため、I/Oパスの挙動を理解するか排他ロックによる明示的なバリア機構の導入が必要です。

なお、非SQPOLLのio_uringの挙動はLinuxカーネル7.3で変更される予定であり、非同期I/Oがプロセス終了後に存続する挙動はなくなる可能性があるとのことです。

・関連記事
Spectre v2対策を突破する新攻撃「TONTOU」が登場、Linuxのパスワードハッシュ漏えいを実証 - GIGAZINE

リーナス・トーバルズが「LinuxはアンチAIではない」「気に入らないならフォークすればいい」などLinuxカーネル開発におけるAI利用について意見を述べる - GIGAZINE

Linuxカーネルで「AIが生成したコードのすべての行、およびそれに起因するバグやセキュリティ上の欠陥の法的責任」を提出した人間がしっかりと負うことに - GIGAZINE

Linux 7.0のMMCに関する変更が「完全にゴミ」としてリーナス・トーバルズに却下される - GIGAZINE

ClaudeのAIアシスタント「Coworkモード」を支えるLinuxコンテナ環境とは? - GIGAZINE

無料でLinuxカーネルの仕組みを学習できる「Linuxカーネルエクスプローラー」 - GIGAZINE

リーナス・トーバルズが「Linux 6.14のリリースを丸一日忘れていた」と謝罪 - GIGAZINE

「ロシア人がLinuxのカーネルメンテナーを解任されている件」についてリーナス・トーバルズが説明 - GIGAZINE

in ハードウェア,   ソフトウェア, Posted by log1c_sh

You can read the machine translated English article Will input/output continue to occur even….