web-dev-qa-db-ja.com

コマンドラインからoom-killerをテストする方法

OOM KillerまたはOut Of Memory Killerは、システムのメモリが非常に少ないときにlinuxカーネルが使用するプロセスです。 ...これにより、プロセスに割り当てられたメモリがアクティブに使用されるようになり、システムメモリの使用が最大化されます。

この自己回答形式の質問では、次のように質問されます。

  • コマンドラインからoom-killerをテストする方法

自己応答でかかる1/2時間よりも速い方法が受け入れられます。

9

OOMキラーをすばやくトリガーするための鍵は、ディスクアクセスによって行き詰まるのを避けることです。そう:

  1. スワップが使用されているときのOOMの動作を具体的にテストすることが目的でない限り、スワップを避けます。テストの前にスワップを無効にしてから、再度有効にすることができます。その後。 _swapon -s_は、現在有効になっているスワップを示します。 _Sudo swapoff -a_はすべてのスワップを無効にします。 _Sudo swapon -a_は通常、それらを再度有効にするのに十分です。

  2. 非スワップディスクアクセスによるメモリアクセスの散在を回避します。そのグロビングベースの方法 は最終的に使用可能なメモリを使い果たします(十分に与えられます)ファイルシステム内のエントリ)、ただし、非常に多くのメモリが必要な理由は、ファイルシステムにアクセスして取得する情報を格納するためです。 SSDを使用している場合でも、スワップがオフになっている場合でも、ディスクからの読み取りに多くの時間が費やされる可能性があります。ディスクアクセスが散在しているメモリアクセスのOOM動作をテストすることが具体的な目標である場合、その方法は妥当であり、おそらく理想的です。それ以外の場合は、目標をはるかに早く達成できます。

スワップを無効にすると、物理ディスクからめったに読み取らないメソッドは非常に高速になります。これには_tail /dev/zero_found by falstaffcommented上記Doug Smythies )キャラクターデバイス_/dev/zero_から読み取りますが、その「デバイス」はnullバイト(つまり、すべてゼロのバイト)を生成するだけで、デバイスノードが開かれると、物理ディスクアクセスを一切含みません。その方法は、tailが入力の末尾の行を探すので機能しますが、ゼロのストリームには改行文字が含まれていないため、破棄される行はありません。

あなたが インタプリタ言語でワンライナーを探している であり、メモリをアルゴリズムで割り当てて設定する場合、あなたは運がいいです。ほぼすべての汎用インタープリター言語では、他の方法を使用せずに、大量のメモリを割り当ててそこに書き込むのは簡単です。これは、_tail /dev/zero_とほぼ同じ速度であると思われるPerlの1行です(私は広範囲にベンチマークしていませんが)。

_Perl -wE 'my @xs; for (1..2**20) { Push @xs, q{a} x 2**20 }; say scalar @xs;'
_

4 GiB=のRAMが搭載された古いマシンでスワップがオフになっていると、それと_tail /dev/zero_の両方を実行するたびに約10秒かかりました。はるかに多くのRAMそれよりも。目標が簡潔であれば、Perlコマンドをはるかに短くすることができます。

そのPerlワンライナーは(_q{a} x 2**20_)の別々の適度に長い文字列(それぞれ約100万文字)を繰り返し生成し、配列(_@xs_)に格納することでそれらをすべて保持します。テスト用に数値を調整できます。使用可能なメモリをすべて使用しない場合、ワンライナーは作成された文字列の総数を出力します。 OOMキラーがPerlを強制終了すると仮定すると、上記の正確なコマンドと邪魔するリソースクォータがないため、実際には常にそうであると私は信じています-その場合、シェルはKilledを表示するはずです。次に、あらゆるOOMの状況と同様に、dmesgに詳細があります。

私はその方法が好きですが、それは Doug Smythiesの回答 にあるような、Cプログラムの作成、コンパイル、および使用に関する有用なものを示しています。高水準のインタプリタ言語では、メモリの割り当てとメモリへのアクセスは別のもののようには感じられませんが、Cでは気づき、必要に応じてそれらの詳細を調査できます。


最後に、OOMキラーが実際にプログラムを強制終了したものであることを常に確認する必要があります。チェックする1つの方法は、dmesgを検査することです。一般に信じられていることとは逆に、メモリを割り当てようとする試みは、たとえLinuxであっても、すぐに失敗する可能性があります。これは明らかに失敗する巨大な割り当てで簡単に発生します...しかし、それらも予期せずに発生する可能性があります。また、一見合理的な割り当てはすぐに失敗する可能性があります。たとえば、私のテストマシンでは、_Perl -wE 'say length q{a} x 3_100_000_000;'_は成功し、_Perl -wE 'say length q{a} x 3_200_000_000;'_は次のように出力します。

_Out of memory!
panic: fold_constants JMPENV_Push returned 2 at -e line 1.
_

どちらもOOMキラーをトリガーしませんでした。より一般的に言えば:

  • プログラムが必要なメモリ量を事前に計算し、それを1回の割り当てで要求した場合、割り当ては成功する可能性があります(その場合、十分なメモリが使用されると、OOMキラーがプログラムを強制終了する場合としない場合があります)、または割り当て単に失敗するかもしれません。
  • 多くの要素を追加して配列を巨大な長さに拡張すると、実際にはOOMキラーがトリガーされることがよくありますが、そうすることでreliablyをテストするのは驚くほどトリッキーです。これがほとんど常に行われる方法は、これが最も効率的な方法であるためです。新しいバッファをそれぞれ、古いバッファの容量x倍の容量で作成します。 xの一般的な値には1.5と2が含まれます(この手法は「テーブルダブリング」とも呼ばれます)。これは、実際に割り当てて使用できるメモリの量と、カーネルが知っている量が多すぎて配布するふりをすることさえできないというギャップを埋めることがあります。
  • メモリの割り当ては、カーネルとはほとんど関係がないか、実際に利用可能なメモリの量が原因で失敗する可能性があり、OOMキラーもトリガーしません。特に、非常に多数の小さな割り当てを正常に実行した後、プログラムは任意のサイズの割り当てですぐに失敗する可能性があります。この障害は、プログラム自体によって(通常はmalloc()などのライブラリ機能を介して)実行される簿記で発生します。 bash配列(実際には二重リンクリストとして実装されています)でのテスト中にbashが終了し、割り当て9バイトが失敗したことを示すエラーメッセージが表示されたときに、これが今日起こったのではないかと思います。

OOMキラーは、意図的にトリガーするよりも、誤ってトリガーする方がはるかに簡単です。

OOMキラーを意図的にトリガーしようとする場合、これらの問題を回避する1つの方法は、メモリを要求しすぎることから始めて、次第に小さくすることです Doug SmythiesのCプログラム と同じです。別の方法は、適度なサイズのメモリのチャンク全体を割り当てることです。これは、上記のPerlワンライナーが行うことです。100万文字の文字列(および舞台裏での追加のメモリ使用量)は特に負担になりません。まとめると、1メガバイトの購入がすべて加算されます。

9
Eliah Kagan

この答えは、Cプログラムを使用して可能な限り多くのメモリを割り当て、その後徐々に実際に使用して、OOM保護から「強制終了」されます。

/*****************************************************************************
*
* bla.c 2019.11.11 Smythies
*       attempt to invoke OOM by asking for a rediculous amount of memory
*       see: https://askubuntu.com/questions/1188024/how-to-test-oom-killer-from-command-line
*       still do it slowly, in chunks, so it can be monitored.
*       However simplify the original testm.c, for this example.
*
* testm.cpp 2013.01.06 Smythies
*           added a couple more sleeps, in attempts to observe stuff on linux.
*
* testm.cpp 2010.12.14 Smythies
*           attempt to compile on Ubuntu Linux.
*
* testm.cpp 2009:03:18 Smythies
*           This is not the first edit, but I am just adding the history
*           header.
*           How much memory can this one program ask for and sucessfully get?
*           Done in two calls, to more accurately simulate the program I
*           and wondering about.
*           This edit is a simple change to print the total.
*           the sleep calls have changed (again) for MS C version 2008.
*           Now they are more like they used to be (getting annoying).
*                                                                     Smythies
*****************************************************************************/

#include <stdio.h>
#include <stdlib.h>

#define CR 13

int main(){
   char *fptr;
   long i, k;

   i = 50000000000L;

   do{
      if(( fptr = (char *)malloc(i)) == NULL){
         i = i - 1000;
      }
   }
   while (( fptr == NULL) && (i > 0));

   sleep(15);  /* for time to observe */
   for(k = 0; k < i; k++){   /* so that the memory really gets allocated and not just reserved */
      fptr[k] = (char) (k & 255);
   } /* endfor */
   sleep(60);  /* O.K. now you have 1 minute */
   free(fptr); /* clean up, if we get here */
   return(0);
}

結果:

doug@s15:~/c$ ./bla
Killed
doug@s15:~/c$ journalctl -xe | grep oom
Nov 11 16:08:24 s15 kernel: mysqld invoked oom-killer: gfp_mask=0x100cca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0
Nov 11 16:08:25 s15 kernel:  oom_kill_process+0xeb/0x140
Nov 11 16:08:27 s15 kernel: [  pid  ]   uid  tgid total_vm      rss pgtables_bytes swapents oom_score_adj name
Nov 11 16:08:27 s15 kernel: oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,global_oom,task_memcg=/user/doug/0,task=bla,pid=24349,uid=1000
Nov 11 16:08:27 s15 kernel: Out of memory: Killed process 24349 (bla) total-vm:32638768kB, anon-rss:15430324kB, file-rss:952kB, shmem-rss:0kB, UID:1000 pgtables:61218816kB oom_score_adj:0
Nov 11 16:08:27 s15 kernel: oom_reaper: reaped process 24349 (bla), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB

実行にはしばらく時間がかかりますが、数分程度です。
Cプログラムでのmlockの使用は役立つかもしれませんが、私は試しませんでした。

テストコンピューターはサーバーなので、watch -d free -m進行状況を監視します。

読者:OOMをいじることはやや危険です。これらのすべての回答とコメントを読むと、付随的な損傷と矛盾に気付くでしょう。他のタスクが少しだけ多くのメモリを要求する可能性がある場合、それを制御することはできません。注意して続行し、これらのタイプのテストの後にコンピュータを再起動することをお勧めします。

7
Doug Smythies

ターミナルで「python」と入力します

次に、このコードをコピーして貼り付け、Enterキーを押します。

var=[]
for x in xrange(99999999999):
    var.append(str(x))

次に行います:

"cat /var/log/messages" and you'll find something like:
Nov 12 11:48:05 TestVM kernel: Out of memory: Kill process 1314 (python) score 769 or sacrifice child
Nov 12 11:48:05 TestVM kernel: Killed process 1314 (python) total-vm:1001264kB, anon-rss:802972kB, file-rss:60kB, shmem-rss:0kB
Nov 12 11:48:49 TestVM kernel: python[1337]: segfault at 24 ip 00007f2ad140c0da sp 00007ffee8c11820 error 6 in libpython2.7.so.1.0[7f2ad1382000+17e000]
3
no_careacc

Oom-killerをトリガーするだけの場合は、次のように「$ a」のサイズを指数関数的に増やします。

bash -c "for b in {0..99999999}; do a=$b$a; done"

ライブで監視したい場合は、次のようなネストされたループを実行する必要があります。

for x in {1..200}; do echo "Round $x"; bash -c "for b in {0..99999999}; do a=$b$a; done"; done

何もコンパイルする必要はありません。バッシュはそれを単独で行うことができます。

予期された結果:

kernel: Out of memory: Kill process 1439 (bash) score 777 or sacrifice child
kernel: Killed process 1439 (bash)

注:残念ながら、これをコメントとして投稿するためのスコアはありません。

1
mjoao

修正された回答

私の最初の答えは実行するのに1/2時間かかりました、そしてこのリビジョンで落とされました:

ls -d /*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*

コマンドラインからoom-killerを呼び出すより高速な方法として、他の誰かの回答を受け入れます。改訂された回答として、journalctlから関連するoom-killerの詳細を取得する方法とその意味を説明します。


これは より効率的な答えmjoao でRAMを使い切る:

logger --tag="kernel" "Start for oom-killer"; a=""; for b in {0..99999999}; do a=$b$a$a$a$a$a$a; done

loggerコマンドは、RAM食べるプロセスが開始したときのタイムスタンプをjournalctlに与えるために追加されました。

Oom-killerが終了したら、新しいターミナルを開いてoomlogを入力します(スクリプトの内容は後で):

$ oomlog
Nov 12 12:29:23 alien kernel[19202]: Start for oom-killer
Nov 12 12:30:02 alien kernel: 31981 total pagecache pages
Nov 12 12:30:02 alien kernel: 11627 pages in swap cache
Nov 12 12:30:02 alien kernel: Swap cache stats: add 10739122, delete 10727632, find 8444277/9983565
Nov 12 12:30:02 alien kernel: Free swap  = 0kB
Nov 12 12:30:02 alien kernel: Total swap = 8252412kB
Nov 12 12:30:02 alien kernel: 2062044 pages RAM
Nov 12 12:30:02 alien kernel: 0 pages HighMem/MovableOnly
Nov 12 12:30:02 alien kernel: 56052 pages reserved
Nov 12 12:30:02 alien kernel: 0 pages cma reserved
Nov 12 12:30:02 alien kernel: 0 pages hwpoisoned
Nov 12 12:30:02 alien kernel: [ pid ]   uid  tgid total_vm      rss nr_ptes nr_pmds swapents oom_score_adj name
Nov 12 12:30:02 alien kernel: [ 4358]  1000  4358  2853387  1773446    5578      13  1074744             0 bash
Nov 12 12:30:02 alien kernel: Out of memory: Kill process 4358 (bash) score 701 or sacrifice child
Nov 12 12:30:02 alien kernel: Killed process 4358 (bash) total-vm:11413548kB, anon-rss:7093784kB, file-rss:0kB, shmem-rss:0kB
Nov 12 12:30:03 alien kernel: oom_reaper: reaped process 4358 (bash), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB

より良い答えは使い切るのに30秒かかりますRAMこれは速すぎず(tail /dev/zeroのように)、遅すぎません(元の答えのように)。

oomlogスクリプトは、journalctl出力の多くのページを16行に圧縮します。

oom-killer[pid]フィールドの説明 here

  • pidプロセスID。
  • uidユーザーID。
  • tgidスレッドグループID。
  • total_vm仮想メモリの使用(4 kBページ)
  • rss常駐メモリの使用(4 kBページ)
  • nr_ptesページテーブルエントリ
  • swapentsエントリをスワップ
  • oom_score_adj通常0;数値が小さいほど、OOMキラーが呼び出されたときにプロセスが停止する可能性が低くなります。

oomlog bashスクリプト

#!/bin/bash

# NAME: oomlog
# PATH: $HOME/askubuntu/
# DESC: For: https://askubuntu.com/questions/1188024/how-to-test-oom-killer-from-command-line
# DATE: November 12, 2019.
# PARM: Parameter 1 can be journalctl boot sequence, eg -b-2 for two boots ago.
#       Defaults to -b-0 (current boot).

BootNo="-b-0"
[[ $1 != "" ]] && BootNo="$1"

# Get time stamp if recorded with `logger` command:
journalctl "$BootNo" | grep 'Start for oom-killer' | tail -n1
# Print headings for last oom-killer
journalctl "$BootNo" | grep '\[ pid ]' -B10 | tail -n11
# Get lat oom_reaper entry's PID
PID=$(journalctl "$BootNo" | grep oom_reaper | tail -n1 | cut -d' ' -f9)
# Print pid information
journalctl "$BootNo" | grep "$PID"']' | tail -n1
# Print summary infomation
journalctl "$BootNo" | grep oom_reaper -B2 | tail -n3
1