3日目の記事を2016/2/2に編集したものです。Linuxにおいてシステムの物理メモリが枯渇したOut-Of-Memory(OOM)という状態になった際の挙動について説明しています。OOMに関連が深いsysctlパラメタを紹介するとともに、カーネルの内部論理についても触れました。本記事に記載されているファイル名は、とくに断りが無ければカーネルソースのトップディレクトリからの相対パス名です。調査に使用したカーネルバージョンは4.3です。本書は話を単純化するために、細かい動作論理については説明を省いていることをご承知おきください。本書の中に誤りを見つけたかた、および、私が追いきれなかったソースについての詳細をご存知のかたは、指摘していただけると助かります。Out-Of-Memory(OOM)とOOM-killerOOM発生時は、デフォルトでは適当に選んだプロセスを殺して物理メモリを空けることにより、なんとかしてシステムを生き延びさせようとします。この仕組みをOOM-killerと呼びます。OOM-killerの挙動には次のような問題があるため、下手にシステムを生き延びさせるよりも、即座にシステムをpanicさせたいことがあります。運用に必須なプロセスが殺されてしまった場合は、システムが生きていてもしょうがないOOM発生後にシステムが生き続けると、そこに至った根本原因を突き止めにくくなる。panicした場合は、その時に採取したカーネルダンプを解析することによって、原因がわかることがあるこのように、OOM発生時にOOM-killerを発動させるのか、それともシステムをpanicさせるのかを選択するsysctlパラメタがvm.panic_on_oomです。いう3つの値をとることができます。それぞれの意味は次の通りです。システムの物理メモリがまだ残っている場合はOOM-killerが発動。panic0と2はわかりやすいのですが、1の、システムの物理メモリがまだ残っているのにOOMが発生するとは、一体どういうことでしょう。それは、以下のように、プロセスのメモリ獲得に制限がかかっている場合です。{set,get}_mempolicyvm.panic_on_oom=1の用途として次のようなものが考えられます。基本的にはOOM発生時にはシステムをpanicさせたい。cgroupよるメモリ使用量を制限したDockerコンテナが複数ある。あるコンテナ内のプロセスがメモリを獲得した際にOOMが発生した場合、そのコンテナ内に影響を留めたい。OOM-killerによって、そのコンテナ内のプロセスのみを殺したい。vm.oom_kill_allocating_taskOOM-killer発動時にシステム内の適当なプロセスを殺すのではなく、メモリを獲得しようとしたプロセスのみを殺害対象にしたい場合があります。そのときはこのsysctlパラメタの値を1にします。カーネル内の実装ここからは、上述の振る舞いについてのカーネル内実装の簡単な解説をします。OOM発生時におけるpanic有無の判断OOM発生時におけるpanic有無の判定をする関数はmm/oom_kill#check_panic_on_oom()です。ソースは以下の通りです。sysctlパラメタに対応します。panic有無は、この値と、メモリ獲得時に存在した制限を示す第二引数constraintによって決まります。constraintが取りうる値はinclude/linux/oom.hに定義されています。};sysctl_panic_on_oomが2の際は、(2)のif文の条件が満たされず、必ず(4)においてpanicが発生します。CONSTRAINT_NONE以外の場合、つまりメモリ獲得時に何らかの制限があった場合は(3)の条件を満たすため、panicせずに復帰します。なければ(4)においてpanicします。OOMが発生した場合に呼ばれる関数OOM発生時には、その原因によって入り口となる関数が2つ存在します。mm/oom_kill.c#out_of_memory()それぞれの場合についてソースを追ってみます。以下はmem_cgroup_out_of_memory()のソースの抜粋です。(1)のchosenは、殺害対象となるプロセスを示します。(2)でpanic有無を判定し、panicしなかった場合は(3)に進んでchosenプロセスを選択します。この後(4)においてOOM-killerが発動し、chosenプロセスを殺します。どのような場合にchosenがNULLになるのか、また、このとき何故プロセスを殺さないままで処理を先に進められるのかについてはソースを追っていないため、不明です。以下、out_of_memory()のソースの抜粋です。CONSTRAINT_MEMPOLICYのいずれかを返します。(2)においては、その結果を用いてpanic有無を判定します。(3)においては、vm.oom_kill_allocating_taskが1の場合(他にも条件は色々ありますが、ここでは省略)には対応するカーネル内変数sysctl_oom_kill_allocating_taskが1になります。値が1の場合は(4)においてメモリを獲得しようとしたプロセスを殺します。(3)の条件を満たさなかった場合は、(5)において、全プロセスの中からOOM-killerによる殺害対象となるプロセスpを適当に選択します。そのようなプロセスが見つからなかった場合は、これ以上システムを生き延びさせることができないため、(6)においてpanicします。見つかった場合は、(7)において選択されたプロセスを殺します。*)-1ULになった場合はプロセスを殺すことなく先の処理に進んでいますが、どのような場合にこうなるのか、また、なぜそれでよいのかについてはソースを追っていないので不明です。upLoginユーザーは見つかりませんでした。