仕事上、例えば vmcore のような、数ギガバイトから数十ギガバイトになる可能性のある巨大なファイルを転送する必要に迫られることがあります。しかし、セキュリティ上の理由でインターネット上のサービスを利用できないという制約があるため、いっそのこと自作してしまえと思い、大容量ファイルのアップロード/ダウンロードを行うためのプログラムを試作することにしました。その際、スクリプト言語などに依存してしまうとEOLへの対応が必要になるため、10年20年先も無改造で使い続けることができるであろうC言語で、そして、メモリリークや Web サーバ本体のバージョンアップの影響を受けないようにするため、リクエスト毎に fork()/execve() する Apache のCGIとして動作するプログラムとして開発することにしました。
しかし、たまにリクエストがエラーになったり、システムの動作が極端に遅くなったりと、どうも様子がおかしいことがありました。発生条件を切り分けていったところ、何と「スリープ処理を入れずに大量のレスポンスの送信を行うだけ」で発生していることが判明しました。親プロセスである Apache のワーカープロセスが、子プロセスであるCGIプログラムの出力を全てメモリ上に保持しようとしてしまい、 OOM killer に殺されるか、さもなくば、後続のリクエストに対する fork() ができなくなるというサービス拒否状態に陥っていたのです。
おいおい、メモリ管理で悩みたくないから子プロセスを使うという選択をしたのに、親プロセスが盛大にメモリを食い潰してどうするのよ〜?
/〜(^x^)〜/
ちなみに、RHELへのバックポートは httpd-2.4.6-76.el7 に含まれる予定になっているため、 RHEL 7.5 のリリース時に修正されるものと予想しています。