メモリの情報取得

このエントリーをはてなブックマークに追加

メモリの情報取得

さて、ここではメモリ周りの情報取得について解説していきます。デバッグ等に役立つ情報も解説します。

システムのメモリの情報取得

まず、システムが知っているメモリ情報を取得しましょう。
例えば、物理メモリ量や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は非常に便利ですので是非活用してください。