解決策 1:
ただし、ゾーンのレイアウトが異なるため、64 ビット O/S (これは 32 ビット) にアップグレードするのが「大ハンマー」アプローチです。
では、ここで OOM を経験した理由についてお答えします。ここには多くの要因が関係しています。
- リクエストの注文サイズと、カーネルが特定の注文サイズをどのように扱うか
- 選択中のゾーン
- このゾーンが使用する透かし
- ゾーン内の断片化
OOM 自体を見ると、明らかに多くの空きメモリが使用可能ですが、OOM キラーが呼び出されていますか?なぜですか?
リクエストの注文サイズと、カーネルが特定の注文サイズを処理する方法
カーネルはメモリを順番に割り当てます。 「順序」は、要求が機能するために満たされなければならない連続した RAM の領域です。オーダーは、アルゴリズム 2^(ORDER + 12)
を使用して、大きさのオーダー (順序という名前) で並べ替えられます。 .したがって、オーダー 0 は 4096、オーダー 1 は 8192、オーダー 2 は 16384 などです。
カーネルには、「高次」と見なされる値がハードコードされています (> PAGE_ALLOC_COSTLY_ORDER
)。これはオーダー 4 以上です (64kb 以上が上位です)。
上位は、下位とは異なり、ページ割り当てに対して満足されます。最新のカーネルでは、メモリの取得に失敗した場合の上位割り当て。
- メモリ圧縮ルーチンを実行して、メモリの最適化を試みます。
- しない 要求を満たすために OOM-killer を呼び出します。
ご注文のサイズはこちらに記載されています
Dec 27 09:19:05 2013 kernel: : [277622.359064] squid invoked oom-killer: gfp_mask=0x42d0, order=3, oom_score_adj=0
オーダー 3 は最下位の要求であり、(ご覧のように) それを満たすために OOM キラーを呼び出します。
ほとんどのユーザー空間の割り当ては、高次のリクエストを使用しないことに注意してください。通常、連続したメモリ領域を必要とするのはカーネルです。これに対する例外は、ユーザー空間が hugepage を使用している場合ですが、ここではそうではありません。
あなたの場合、パケットをネットワーク スタックにキューイングしようとするカーネルによって、オーダー 3 の割り当てが呼び出されます。そのためには 32kb の割り当てが必要です。
選択中のゾーン
カーネルはメモリ領域をゾーンに分割します。 x86 では、メモリの特定の領域が特定のハードウェアによってのみアドレス指定可能であるため、この分割が行われます。たとえば、古いハードウェアでは、「DMA」ゾーンのメモリしかアドレス指定できない場合があります。メモリを割り当てたい場合、最初にゾーンが選択され、のみ このゾーンの空きメモリは、割り当てを決定する際に考慮されます。
私はゾーン選択アルゴリズムについて完全に理解しているわけではありませんが、典型的なユースケースは DMA から割り当てることではなく、通常は要求を満たすことができる最もアドレス可能なゾーンを選択することです.
OOM 中に大量のゾーン情報が吐き出され、/proc/zoneinfo
から収集することもできます .
Dec 27 09:19:05 2013 kernel: : [277622.359382] DMA free:2332kB min:36kB low:44kB high:52kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:15968kB managed:6960kB mlocked:0kB dirty:0kB writeback:0kB mapped:0kB shmem:0kB slab_reclaimable:8kB slab_unreclaimable:288kB kernel_stack:0kB pagetables:0kB unstable:0kB bounce:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? yes
Dec 27 09:19:05 2013 kernel: : [277622.359393] Normal free:114488kB min:3044kB low:3804kB high:4564kB active_anon:0kB inactive_anon:0kB active_file:252kB inactive_file:256kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:894968kB managed:587540kB mlocked:0kB dirty:0kB writeback:0kB mapped:4kB shmem:0kB slab_reclaimable:117712kB slab_unreclaimable:138616kB kernel_stack:11976kB pagetables:0kB unstable:0kB bounce:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:982 all_unreclaimable? yes
Dec 27 09:19:05 2013 kernel: : [277622.359404] HighMem free:27530668kB min:512kB low:48272kB high:96036kB active_anon:2634060kB inactive_anon:217596kB active_file:4688452kB inactive_file:1294168kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:36828872kB managed:36828872kB mlocked:0kB dirty:0kB writeback:0kB mapped:183132kB shmem:39400kB slab_reclaimable:0kB slab_unreclaimable:0kB kernel_stack:0kB pagetables:430856kB unstable:0kB bounce:367564104kB free_cma:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? no
HighMemゾーンは64ビットには存在しないため、DMA、Normal、およびHighMemのゾーンは32ビットプラットフォームを示しています。また、64ビットシステムでは、通常は4GB以上にマップされますが、32ビットでは最大896Mbにマップされます(ただし、カーネルはこれよりも小さな部分しか管理していないと報告しています:- managed:587540kB
.)
最初の行 gfp_mask=0x42d0
をもう一度見ると、この割り当てがどこから来たのかを知ることができます。 どのタイプの割り当てが行われたかを示します。最後のバイト (0) は、これが通常のゾーンからの割り当てであることを示しています。 gfp の意味は、include/linux/gfp.h にあります。
このゾーンが使用する透かし。
メモリが不足している場合、それを再利用するためのアクションはウォーターマークによって指定されます。ここに表示されます:min:3044kB low:3804kB high:4564kB
.空きメモリが「低」に達すると、「高」のしきい値を超えるまでスワッピングが発生します。メモリが「分」に達した場合、OOM キラーを介してメモリを解放するために、ものを強制終了する必要があります。
ゾーン内の断片化。
特定の順序のメモリに対する要求が満たされるかどうかを確認するために、カーネルは各順序で使用可能な空きページ数と使用可能なページ数を考慮します。これは /proc/buddyinfo
で読めます .ここに示すように、OOM-killer レポートはさらに buddyinfo も吐き出します:
Normal: 5360*4kB (UEM) 3667*8kB (UEM) 3964*16kB (UEMR) 13*32kB (MR) 0*64kB 1*128kB (R) 1*256kB (R) 0*512kB 0*1024kB 0*2048kB 0*4096kB = 115000kB
メモリ割り当てが満たされるには、する必要があります 要求された注文サイズまたはより高い割り当てで使用可能な空きメモリである必要があります。下位に大量の空きデータがあり、上位に何もないということは、メモリが断片化されていることを意味します。非常に高次の割り当てを取得すると、利用可能な高次のページがないため、(多くの空きメモリがあっても) 満たされなくなる可能性があります。カーネルは、アドレス指定可能な RAM スペースにギャップを残さないように、多数の下位ページを移動することによってメモリを最適化できます (これはメモリ コンパクションと呼ばれます)。
OOM-killer が呼び出されましたか?なぜですか?
したがって、これらのことを考慮に入れると、次のように言えます。
- 32kB の連続割り当てが試行されました。ノーマルゾーンから
- 選択したゾーンに十分な空きメモリがありました。
- オーダー 3、5、および 6 のメモリが利用可能でした
13*32kB (MR) 1*128kB (R) 1*256kB (R)
あった 空きメモリ、その他の注文可能 要求を満たす。どうしたの?
注文からの割り当てには、その注文またはそれ以上で使用可能な空きメモリの量をチェックするだけではありません。カーネルは、空きラインの合計からすべての下位オーダーのメモリを効果的に差し引き、残ったものに対して最小ウォーターマーク チェックを実行します。
あなたのケースでは、私たちがしなければならないそのゾーンの空きメモリをチェックすることです.
115000 - (5360*4) - (3667*8) - (3964*16) = 800
この空きメモリ量は、min
に対してチェックされます。 つまり、技術的に言えば、要求した割り当てを実行するための空きメモリが残っていません。これが、OOM-killer を呼び出した理由です。
修正中
2 つの修正があります。 64 ビットにアップグレードすると、「通常」が 4 GB から最大 36 GB になるようにゾーンのパーティション分割が変更されるため、非常に断片化される可能性があるゾーンにメモリ割り当てを「デフォルト設定」することはありません。この問題を解決するアドレス可能なメモリが増えたからではなく (すでに PAE を使用しているため)、選択したゾーンのアドレス可能なメモリが増えただけです。
2 番目の方法 (テストしたことはありません) は、カーネルにメモリをより積極的に圧縮させることです。
vm.extfrag_threshold
の値を変更すると 500 から 100 に変更すると、上位の割り当てを優先するためにメモリを圧縮する可能性が高くなります。ただし、これまでこの値を台無しにしたことはありません。これは、/sys/kernel/debug/extfrag/extfrag_index
で利用可能な断片化インデックスが何であるかにも依存します。 .現時点では、これ以上のものを提供するものを確認するのに十分な新しいカーネルを備えたボックスはありません.
あるいは、ある種の cron ジョブ (これは恐ろしく、恐ろしく醜い) を実行して、/proc/sys/vm/compact_memory
に書き込むことで手動でメモリを圧縮することもできます。 .
正直なところ、この問題を回避するためにシステムを調整する方法が実際にあるとは思いません。このように動作するのはメモリアロケータの性質です。使用するプラットフォームのアーキテクチャを変更することが、おそらく唯一の根本的に解決可能な解決策です。
解決策 2:
始めに:本当にすべきです 64 ビットのオペレーティング システムを使用してください。ここで 32 ビットにとどまる正当な理由はありますか?
この問題を診断するのは、システムをより詳しく、できれば障害が発生した頃に行わなければ難しいため、私の (簡単な) 投稿は多かれ少なかれ一般的に 32 ビット システムでのメモリの問題を対象としています。 64 ビットに移行すると、この問題がすべて解決することを説明しましたか?
あなたの問題は 3 つあります。
まず第一に、PAE カーネルでも、プロセスごとのアドレス空間は 4GiB に制限されています[1]。これは、squid インスタンスがプロセスごとに 4GiB を超える RAM を消費できないことを意味します。私は squid にはあまり詳しくありませんが、これがメインのプロキシ サーバーである場合は、とにかく十分ではないかもしれません.
次に、大量の RAM を備えた 32 ビット システムでは、ZONE_HIGHMEM のメモリを使用するために必要なデータ構造を格納するために、「ZONE_NORMAL」と呼ばれるメモリが大量に使用されます。これらのデータ構造は、カーネルが独自の目的で使用するメモリは常に ZONE_NORMAL (つまり、最初の 1GiB っぽい) にある必要があるため、ZONE_HIGHMEM 自体に移動することはできません。 ZONE_HIGHMEM のメモリが多いほど (この場合は大量)、これが問題になります。これは、カーネルが ZONE_HIGHMEM を管理するために ZONE_NORMAL からより多くのメモリを必要とするためです。 ZONE_NORMAL の空きメモリ量がなくなると、システムが一部のタスクで失敗する可能性があります。ZONE_NORMAL は ロット のものが 32 ビット システムで発生します。カーネル関連のすべてのメモリ操作、たとえば;)
第 3 に、ZONE_NORMAL にいくらかのメモリが残っていても (ログを詳しく調べていません)、一部のメモリ操作では断片化されていないメモリが必要になります。たとえば、すべてのメモリが非常に小さな断片に断片化されている場合、それ以上を必要とする一部の操作は失敗します。 [3] ログを簡単に見ると、ZONE_DMA と ZONE_NORMAL でかなりの断片化が見られます。
編集:上記の Mlfe の回答には、これがどのように機能するかについての優れた説明があります。
繰り返しますが、64 ビット システムでは、すべてのメモリが ZONE_NORMAL にあります。 64 ビット システムには HIGHMEM ゾーンはありません。問題は解決しました。
編集:ここ [4] を見て、重要なプロセスをそのままにしておくように oom-killer に指示できるかどうかを確認できます。これですべてが解決するわけではありませんが (もしあれば)、試してみる価値はあります。
[1] http://en.wikipedia.org/wiki/Physical_address_extension#Design
[2] http://www.redhat.com/archives/rhelv5-list/2008-September/msg00237.html および https://access.redhat.com/site/documentation/en-US/Red_Hat_Enterprise_Linux/5/html /Tuning_and_Optimizing_Red_Hat_Enterprise_Linux_for_Oracle_9i_and_10g_Databases/sect-Oracle_9i_and_10g_Tuning_Guide-Hardware_Architectures_and_Linux_Kernels-a32_bit_Architecture_and_the_hugemem_Kernel.html
[3] http://bl0rg.krunch.be/oom-frag.html
[4] http://lwn.net/Articles/317814/
解決策 3:
@MIfe は、カーネルでのメモリ割り当てがどのように処理されるかについての優れた記事を既に提供しており、64 ビット OS への切り替えや /proc/sys/vm/compact_memory
による手動メモリ圧縮のような厄介なハックなどの適切なソリューションも提供しています。 cron
で .
私の2セントはあなたを助けるかもしれない別の回避策になるでしょう:
tcp_tso_segment
があることに気付きました カーネルのバックトレースで、次のようにします:
# ethtool -K ethX tso off gso off lro off
mm
へのプレッシャーを減らすことができます より低いオーダーを使用するように強制することによって.
追伸 .すべてのオフロードのリストは # ethtool -k ethX
で取得できます
解決策 4:
パニックは、sysctl "vm.panic_on_oom =1" が設定されているためです。これは、システムを再起動すると正常な状態に戻るという考えです。これは sysctl.conf で変更できます。
一番上に、squid によって呼び出された oom killer が表示されています。 squid の構成とその最大メモリ使用量を確認してください (または単に 64 ビット OS に移行してください)。
/proc/meminfo は、使用中のハイ メモリ ゾーンを示しているため、36GB メモリで 32 ビット カーネルを実行しています。また、通常ゾーンでは、squid のメモリ要求を満たすために、カーネルが 982 ページをスキャンしましたが、成功しなかったこともわかります:
pages_scanned:982 all_unreclaimable? yes