メモリの情報取得
ツイートメモリの情報取得
さて、ここではメモリ周りの情報取得について解説していきます。デバッグ等に役立つ情報も解説します。
システムのメモリの情報取得
まず、システムが知っているメモリ情報を取得しましょう。
例えば、物理メモリ量やSWAPのメモリ量等です。時々ですが、こういう情報が欲しい場合がありますね。
sysinfo(2)のmanページを見ればどういった情報が取得できるかが分かります。
Linux 2.3.23 (i386)、2.3.48 (全てのアーキテクチャ) からは構造体は struct sysinfo { long uptime; /* Seconds since boot */ unsigned long loads[3]; /* 1, 5, and 15 minute load averages */ unsigned long totalram; /* Total usable main memory size */ unsigned long freeram; /* Available memory size */ unsigned long sharedram; /* Amount of shared memory */ unsigned long bufferram; /* Memory used by buffers */ unsigned long totalswap; /* Total swap space size */ unsigned long freeswap; /* swap space still available */ unsigned short procs; /* Number of current processes */ unsigned long totalhigh; /* Total high memory size */ unsigned long freehigh; /* Available high memory size */ unsigned int mem_unit; /* Memory unit size in bytes */ char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding to 64 bytes */ }; となり、大きさは mem_unit バイトの倍数で与えられる。 sysinfo() はシステム全体の統計を取得する簡単な方法を提供する。 これは /dev/kmem を読むよりも移植性の高い方法である。
#include <stdio.h> #include <sys/sysinfo.h> int main(void) { struct sysinfo info; sysinfo(&info); printf("mem_unit:%d\n", info.mem_unit); printf("totalram:%ld\n", info.totalram); printf("freeram:%ld\n", info.freeram); printf("bufferram:%ld\n", info.bufferram); printf("totalswap:%ld\n", info.totalswap); printf("freeswap:%ld\n", info.freeswap); return 0; }
簡単に情報が取得できますね。
プロラムが動作中に、情報を取得したいという要求は時々ありますので、是非活用してください。
注意事項としてsysinfo(2)はLinux特有なのでOSを超えて移植はできません。
また、free(3)を実行したからといってここで取得できる値(freeram)が必ずしも変化するとは限らないことにも注意してください。
これは、malloc(3)/free(3)の実装次第です。free(3)をしても直ぐにシステムに返さずにおいておき、効率良く再度malloc(3)された時のためにライブラリ側が持っておくのです。
とは言え、メモリリークのチェックなどには役に立つでしょう。
メモリトレース
次は、メモリトレースの機能です。メモリの取得開放をログに出力してくれます。
GNU依存なのですが、Linuxであればまず問題ありませんし、特別な外部ライブラリやツールなども必要ありません。
使い方は、ログ出力をしたい範囲をmtrace(3)、muntrace(3)で囲みます。
#include <stdio.h> #include <mcheck.h> #include <stdlib.h> void malloc_func(void) { char *p; char *rp; //必要な領域を取得 p = malloc(128); if (p == NULL) { return; } //取得したメモリ領域を操作 snprintf(p, 128, "abcdefg"); //文字列を書き込んでみよう printf("%s\n", p); //メモリサイズの変更 rp = realloc(p, 256); if (rp == NULL) { free(p); return; } p = rp; //不必要になったら開放 //free(p); //メモリリークを発生させる } int main(void) { //メモリチェック開始 mtrace(); malloc_func(); //メモリチェック終了 muntrace(); return 0; }
使い方は、まずにコンパイル時に"-g"が必要です。そして実行時に環境変数MALLOC_TRACEでログファイル名を指定する必要があります。
$ gcc -g -o mcheck mcheck.c $ MALLOC_TRACE='mcheck.log' ./mcheck
ログの見方ですが、'+'はメモリ取得を、'-'はメモリ解放を表します。ちなみにC++のnew、deleteでも使用できます。
ただ、ログ自体は見づらいので、mtrace(1)コマンドを使用して見やすくなっています。
$ cat mtrace.log = Start @ ./mcheck:[0x804850e] + 0x85b8378 0x80 @ ./mcheck:[0x8048545] < 0x85b8378 @ ./mcheck:[0x8048545] > 0x85b8378 0x100 = End $ mtrace ./mcheck mcheck.log Memory not freed: ----------------- Address Size Caller 0x085b8378 0x100 at mcheck.c:21
メモリリークを発見できているのが分かります。freeのコメントアウトを外せば正しく動作していることも分かります。
簡単にメモリリークをチェックでき、ほぼプログラムに影響の無い方法です。
欠点としてはGNU依存ですので、libc等を使う環境では使えないこと(その場合以前の様に自前で実装する必要があるかもしれない)、他のチェックツールと併用できない場合もあること、があります。
動的なメモリのサイズ取得
これは良くある欲求です。通常であれば、malloc(3)等で動的に取得したメモリサイズは後で知ることは出来ません。
どうしても必要であれば何らかの方法(おそらく構造体)で保存しておくしかありません。
しかし、GNUの拡張ではmalloc_usable_size(3)を使用することにより簡単に取得ができます。
#include <stdio.h> #include <malloc.h> int main(void) { char *p; p = malloc(128); if (p == NULL) { return 1; } malloc_size = malloc_usable_size(p); printf("%d\n", malloc_size); free(p); return 0; }
$ ./get_malloc_size 132
取得したメモリサイズは128ですが、malloc_usable_size(3)で取得した値は132になっています。
これは汎用的に使えるようにアライメントされているからです。正確な値が必要であれば、アライメントの分を自前で最計算すればよいでしょう。
まとめ
GNU拡張が多いですが、どれも非常に便利な機能です。
こういった情報は少ないですが、探すコツはmanの関連項目です。manは非常に便利ですので是非活用してください。