はじめに
仕事で、担当しているシステムがハングアップしたような事象が起こりました。その際、sarコマンドの結果を見て、「メモリが枯渇している」という説明を受けたのに対して違和感を感じました。
freeでの使用状況は枯渇していないことを示していますし、スワップ使用量が増えるようなこともありませんでした。一方で、自分の説明に説得力をもたせることができなかったので、ソースコードを調べて観ました。
誤り等見つけたらご指摘いただけるとありがたいです。
対象サーバ
Redhat Enterprise Linux 7.7
freeコマンド
freeはRHEL6系とRHEL7系で出力結果が変わって、7系では使用量にキャッシュ等は含まない、その時点で利用可能なメモリ量をavailable で示してくれるようになったと理解しています。
今回確認したソースコードは以下のURLからとってきました。後から気づいたのですが、サーバのfreeのバージョンを調べて該当のソースを読むべきでした。
https://gitlab.com/procps-ng/procps
ざっくりとした構造
freeはコマンドなので、main関数が起点ですね。なので、そこから読みました。
今回、freeをオプション等なしで実行したパターンを想定したので、オプション解析などはすべてスキップします。そのため、重要なのは326行目からのdo while ループです。
見ると分かりますが、353行目以降は、出力のためのコードで、メモリの値を取り出しているのは349行目の meminfo() だけと推測しました。
meminfo関数
定義は、同じディレクトリのsysinfo.c に書かれていました。細かいことは省くと、おそらく754行目からのforループが/proc/meminfo の情報をmemtableの各変数にセットしているように思いました。
for(;;){
tail = strchr(head, ':');
if(!tail) break;
*tail = '\0';
if(strlen(head) >= sizeof(namebuf)){
head = tail+1;
goto nextline;
}
strcpy(namebuf,head);
found = bsearch(&findme, mem_table, mem_table_count,
sizeof(mem_table_struct), compare_mem_table_structs
);
head = tail+1;
if(!found) goto nextline;
*(found->slot) = (unsigned long)strtoull(head,&tail,10);
nextline:
tail = strchr(head, '\n');
if(!tail) break;
head = tail+1;
}
kb_main_used が kb_main_total から (kb_main_free と kb_main_cached と kb_main_buffers の合計)を引いているのと、kb_main_cached が kb_page_cache と kb_slab_reclaimable の和になるように計算していますが、それ以外は、/proc/meminfoの値をそのまま使っているように思いました。
freeの使用量にはキャッシュやバッファを含まないと結論付けました。
topコマンド
次にtopコマンドを調べました。なんとなくfreeと同じだろうと予測があったので、ざくっとしか読んでいません。
top.cのmain関数から、frame_make() → sysinfo_refresh(int forced) → meminfo() と遷移しているのを追えました。このmeminfoはfreeで読んだものと同じなので、おそらくtopとfreeの出力はだいたい同じだと理解しました。
sarコマンド
さて問題のsarコマンドです。sarの出力は、キャッシュなども使用量に含むので、freeとかよりも使用率が高く出るでしょ、という点を確認したかったのでした。
今回確認したソースコードは以下のURLからとってきました。
https://github.com/sysstat/sysstat
最初、sar.c のmain関数から読み始めたのですが、いろいろ複雑な処理をしているように見えたので、途中で方針を変えて、出力する部分を中心に読むことにしました。どうやらpr_stats.c が出力部分を担当しているようなので、この中の stub_print_memory_stats 関数から読み始めました。
すると、526行目で空きメモリを計算して、534行目でシステムのメモリ総量から空きメモリを引いて、使用量を計算しています。あれれ?これだとfreeと一緒だな、となりちょっと困惑。
その後で、ChangeLogを読んでみると、version11.7.4でfree(1)やtop(1)の出力と合わせるように、計算方法変えましたという記述がありました。
https://github.com/sysstat/sysstat/blob/master/CHANGES#L371
対象のサーバのバージョンを調べると sysstat バージョン 10.1.5 だっったので、該当のバージョンのファイルを調べると、キャッシュとかバッファとか込みで計算しているのが確認できました。
https://github.com/sysstat/sysstat/blob/v10.1.5/pr_stats.c#L443
なお、メモリ使用状況の情報源はfreeとかtopと同様に/proc/meminfo のように思いました。(自信ないです。)
まとめ
バージョン11.7.4より前のsarでは、使用量にキャッシュやバッファを含んでいるので、その使用率を見て「メモリが逼迫している」と言ってはいけないと結論づけました。
Linuxのメモリ管理やキャッシュの仕組みの理解が甘いことを痛感したので、この本を読み直そうと思います。「詳解 システム・パフォーマンス」も読みかけですし、しっかりと理解できるようにしたいと思います。
- [試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識
https://gihyo.jp/book/2018/978-4-7741-9607-7