プロセスあたりのメモリ使用量を知りたい
tl;dr
それぞれのプロセスがどのぐらいのメモリを使用しているかを知りたい。
コマンドで確認する
システム全体のメモリ使用量を確認する
- cat /proc/meminfo
linuxではデバイスもプロセスもファイルとして扱えるので、catできる。
MemTotal: 8073996 kB MemFree: 333772 kB MemAvailable: 4262036 kB Buffers: 600280 kB Cached: 3386552 kB SwapCached: 0 kB Active: 4668596 kB Inactive: 2382920 kB Active(anon): 3061940 kB Inactive(anon): 286724 kB Active(file): 1606656 kB Inactive(file): 2096196 kB Unevictable: 0 kB Mlocked: 0 kB
- free -tm
total used free shared buffers cached Mem: 7884 6293 1591 260 470 3368 -/+ buffers/cache: 2454 5430 Swap: 2054 0 2054 Total: 9939 6293 3646
上のを高級な表示にした感じ。linuxではbuffersとcachedは、メモリが足りない時は開放するようになるので、buffers/cache
の値を見るとよい。
- vmstat
仮想メモリの統計を得る。メモリに関してはfreeと同じような感じだが、cpuやswap, ioの状況も見ることができる。
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 2 0 0 2911928 490096 2719356 0 0 48 44 356 16 8 2 87 3 0
またvmstat [sec]
として秒数を引数に渡すと、その秒数ごとの出力を得ることができる。
プロセスごとのメモリ使用量を確認する。
用語を以下のように置くことにする。
- VSS: 仮想メモリの使用量
- RSS: 物理メモリの使用量(共有メモリの使用量を含める)
- PSS: 物理メモリの使用量(共有メモリの使用量をプロセス間で等分する)
- USS: 物理メモリの使用量(共有メモリの使用量を除く)
使用量というのは実際にはページ単位で使われているかであって、Linuxでは4KBを1ページとしているため最小単位は4KBとなる。HugePagesを使えば、より粒度の大きいページを使うことができて、そうすることでページ表へのアクセスを減らすことができ、場合によっては高速化できる。
ここで検知したいのはvirtual memoryで確保した領域ではなく、実際に使われている領域がどのぐらいかということにある。そのため最低でもRSS、欲を言えばそれより細かい粒度での情報が欲しい。
- ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.0 35840 5612 ? Ss 12:43 0:02 /usr/lib/systemd/systemd
これによってRSSは分かるが、結局それは正しい実使用量ではない。
Virtual Threads: Understanding memory usage on Linux
それは先述のように共有ライブラリの使用量が含まれており、reservedなメモリ使用量を全て表示しているからだ。C,C++のソフトウェアに関してはgdb, valgrindなどのデバッガを使えばよいとしている。
- top/htop
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 3296 root 20 0 2330052 981008 162780 S 35.43 12.15 102:10.74 firefox 1959 root 20 0 3283584 575640 43724 S 6.954 7.130 6:17.59 dropbox
topコマンドよりhtopコマンドの方が視覚的にわかりやすいプロセス毎のメモリ/cpu使用率などを表示してくれる。RESがタスクが使用しているスワップされていない物理メモリ、SHRにタスクが利用している共有メモリの総量が載っているので、USS=RES-SHRでとりあえず計算することは可能。
- pmap -x {procid}
3296: firefox START SIZE RSS PSS DIRTY SWAP PERM MAPPING 00007f4d21e00000 11264K 9320K 9320K 9320K 0K rw-p [anon] 00007f4d23400000 10240K 8120K 8120K 8120K 0K rw-p [anon] 00007f4d23e50000 4K 0K 0K 0K 0K ---p [anon] 00007f4d23e51000 8192K 20K 20K 20K 0K rw-p [anon] 00007f4d24651000 4K 0K 0K 0K 0K ---p [anon] 00007f4d24652000 8192K 12K 12K 12K 0K rw-p [stack:22843] 00007f4d24e52000 4K 0K 0K 0K 0K ---p [anon] 00007f4d24e53000 8192K 12K 12K 12K 0K rw-p [stack:22673] 00007f4d25653000 7860K 428K 428K 0K 0K r--p /usr/share/fonts/truetype/ipamp.ttf 00007f4d25e00000 5120K 0K 0K 0K 0K rw-p [anon] 00007f4d26700000 22528K 9512K 9512K 9512K 0K rw-p [anon] 00007f4d27e00000 5120K 2252K 2252K 2252K 0K rw-p [anon] 00007f4d28c00000 1024K 816K 816K 816K 0K rw-p [anon] 00007f4d28df7000 6092K 2572K 335K 0K 0K r--p /usr/share/fonts/truetype/ipagp.ttf
- smem
PID User Command Swap USS PSS RSS 1093 hoge /usr/lib32/skype/skype 0 172280 175262 182704 1050 hoge /usr/bin/dropbox 0 193672 195052 207152
それぞれのプロセスのUSS, PSS, RSSを表示してくれる。python2製。
上のStackOverflowによれば、smemでは下の計算式で求められている。
USS = sum of /proc/<pid>/smaps Private_clean + Private_dirty PSS = sum of /proc/<pid>/smaps Pss RSS = sum of /proc/<pid>/smaps Rss
smapsで得られる共有メモリの部分には、共有ライブラリだけでなく例えばforkした子プロセスでは、物理メモリのデータをCopy On Write(実際にメモリが書き換えられた時点で複製する)ようにしているので、そのときに共有されている物理メモリについても含まれている。そのためUSSだけがそのプロセスが実際に使うメモリ量とはならないし、RSSであっても共有ライブラリの分のメモリを含んでいる以上、同様となる。
dtrace
さてSolarisやMac OS X、FreeBSDにはdtrace
というコマンドがある。これは動的にシステムをトレースするソフトウェアであるが、残念ながらlinuxには搭載されていない。
以下のツールは、dtrace
の代替として紹介されている。それぞれを紹介したい。これを用いて、メモリを確保するようなシステムコールがいつどれだけ発行されたかをたどることができる。
- sysdig
csysdig
でncurseによるインタラクティブなモニタリングをすることが可能となっている。そう、sysdig
で表示される謎の出力をいちいちgrepしたり、引数をつけて呼び出す必要はないのだ。
最初に開いた画面でできることはたとえば以下がある。
F2
を押すと、Viewを変更できる。例えば何らかのプロセスによってopenしているファイルのリスト(lsof
)であったり、Systemcallのリストであったり。
Enter
キーで選択されている行に限定して詳しく見ることができる。BackSpace
キーで戻れる。例えばSystemCallのリストで、Systemcallを選んでEnter
を押すと、そのSystemcallを読んだプロセスが表示される。
F5
を押すと、read
とwrite
イベントを見ることができる。F6
を押すと、本家sysdigの出力を垣間見ることができる。
なお、実行するにはsuperuser権限で実行しなければならない。
stap
とかいうどこかで聞いたことの有るような細胞名のコマンドで呼び出すこのツールは、Systemtap Scriptと呼ばれるスクリプトファイルに従ってカーネルに対する探索を行うことが可能である。
がまだ詳しくわかっていない...
- strace
実際にシステムコールがどのタイミングで走っているかについてはstrace
で検知することが可能である。プロセスにアタッチして、そのプロセスにおけるシステムコールを確認することもできる。
straceを使うと、このような形で呼ばれたシステムコールと戻り値をみることができる。それをもとにmmap
やbrk
がどれだけメモリ確保したかを累算することができるが、いかんせん、これが内部で呼んでいるシステムコールのptrace
は遅いので、実用性という面では厳しい。
brk(NULL) = 0x2156000 brk(0x2188000) = 0x2188000 mmap(NULL, 1000000000004096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory) brk(0x38d7ea6df0000) = 0x2188000 mmap(NULL, 1000000000135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory) mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x7f73a4490000 munmap(0x7f73a4490000, 62324736) = 0 munmap(0x7f73ac000000, 4784128) = 0 mprotect(0x7f73a8000000, 135168, PROT_READ|PROT_WRITE) = 0 mmap(NULL, 1000000000004096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory) exit_group(0) = ?
さいごに
今回の記事については、素人同然の立場でまとめた備忘録ですので、Linuxにおけるメモリの使用方法について不正確な記述が多いおそれがあります。ご注意下さい。