Linuxにおける各種メモリについて
Linuxで扱う様々な種類や単位のメモリについてまとめておく。また、メモリというリソースに関する制御や管理について考察する。
ulimit
$ ulimit -a | egrep "memory|stack" max locked memory (kbytes, -l) 64 max memory size (kbytes, -m) unlimited stack size (kbytes, -s) 8192 virtual memory (kbytes, -v) unlimited
- max locked memory -
mlock
システムコールを使うことにより、メモリの内容がディスク上にスワップされないようにする(=Lockする)ことができる。その最大容量。 - max memory size - 静的(スタック領域)や動的(ヒープ領域)に確保するメモリの最大容量。
- virtual memory - スワップされたプロセスが使用するディスク上の仮想メモリ空間の最大容量。
- stack size - 静的(スタック領域)に確保するメモリの最大容量。
ulimitの機能
- シェル組み込みの機能で、ユーザ毎にCPUやメモリ、ファイル、PIPEなどのリソースの使用量に制限を設ける。
- ソフトリミットはハードリミットを超えない範囲で設定が可能で、ハードリミットを上げることができるのはrootのみ。
- ulimitの制御は、setrlimit/getrlimitシステムコールを使って行われる。
$ strace bash -c "ulimit -S -s 65535" 2>&1 | grep -i rlimit getrlimit(RLIMIT_NPROC, {rlim_cur=1024, rlim_max=3897}) = 0 getrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0 getrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0 setrlimit(RLIMIT_STACK, {rlim_cur=65535*1024, rlim_max=RLIM64_INFINITY}) = 0 $ strace bash -c "ulimit -H -s 819200" 2>&1 | grep -i rlimit getrlimit(RLIMIT_NPROC, {rlim_cur=1024, rlim_max=3897}) = 0 getrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0 getrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0 setrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=819200*1024}) = 0
- なお、ディスク(パーティション)の使用量制限に関してはulimitではなくquotaで行う。
quota
- ユーザ単位・グループ単位でディスク(パーティション)の使用量に制限を設ける。
- mountオプションとしてusrquota・grpquotaを指定する必要がある。
- ソフトリミットは猶予期間内であれば超えることができるが、猶予期間を超えるとソフトリミットがハードリミットとなる。
- ハードリミットは超えることができない。
- quotaの管理は、パーティションのトップディレクトリに置かれるaquota.user, aquota.groupファイル上で行われる。
ps / pmap / time*1
$ ps aux | head -n1 USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND $ pmap -X $(pidof bash) | head -n2 2571: pmap -X 2571 2073 Address Perm Offset Device Inode Size Rss Pss Referenced Anonymous Swap Locked Mapping $ /usr/bin/time -apv ls 2>&1 | grep resident Maximum resident set size (kbytes): 2428 Average resident set size (kbytes): 0
- VSS/VSZ (Virtual Set Size) - プロセスの仮想メモリ全体のサイズ。実際には使用していない領域を含んでいる。
- RSS (Resident Set Size) - プロセスが仮想メモリの中で、実際に使用しているメモリのサイズ。共有ライブラリのメモリを含むため、各プロセスで重複してカウントされている。また、スワップアウトされているメモリは含まれない。
PSS (Proportional Set Size) - プロセスが実際に使用しているメモリのサイズ。共有ライブラリのメモリを含むが、そのライブラリを使用しているプロセス数で割っているため、RSSの問題点は解決している。
(例)dhclientとcupsdはlibcryptoを共有ライブラリとして利用しており、それぞれのプロセスのRSSとしてカウントされている。
$ ldd /sbin/dhclient | grep libcrypto libcrypto.so.10 => /usr/lib64/libcrypto.so.10 (0x00007f62d75f7000) $ ldd /usr/sbin/cupsd | grep libcrypto libcrypto.so.10 => /usr/lib64/libcrypto.so.10 (0x00007fd442cef000) $ ps -FC dhclient UID PID PPID C SZ RSS PSR STIME TTY TIME CMD root 2394 842 0 25553 19040 0 23:27 ? 00:00:00 /sbin/dhclient -d -sf /usr/libexec/nm-dhcp-helper -pf /var/run/dhclient-p2p1.pid -lf /var/lib/ $ ps -FC cupsd UID PID PPID C SZ RSS PSR STIME TTY TIME CMD root 1709 1 0 47262 2044 0 23:11 ? 00:00:00 /usr/sbin/cupsd -f $ man ps ... rss RSS resident set size, the non-swapped physical memory that a task has used (inkiloBytes). (alias rssize, rsz). ...
pmapのRSSとSizeは、実際にメモリに読み込まれているかどうかの違いがある。この違いは、デマンドページングによりページフォルトが発生するまで実際のメモリへの読み込みを遅延する、ことによって現れる。それによって、例えば共有ライブラリの中の実際に使用する関数のみをメモリに読み込んで実行することができる。Sizeは実際には読み込まれておらず、論理アドレスに割り当てただけの部分もカウントしている。
デマンドページングは
malloc
発行時にも効果を発揮する。malloc
を発行すると論理アドレス上ではメモリを割り当てたことになるが、物理メモリ上ではまだメモリ領域は確保されず、実際にメモリに書き込んだ際、初めて物理メモリ上でメモリ領域が確保される。この機構により実際の物理メモリ容量よりも大きいメモリをmalloc
により割り当てることができ、これをオーバーコミットと呼ぶ。ただし、実際に物理メモリ以上の領域に書き込もうとすると、メモリ不足の例外が発生するか、OOM Killerにより停止されるだろう。オーバーコミットの容量はsysctlである程度制御が可能である。
/proc/meminfo
$ cat /proc/meminfo | egrep -i "active|dirty|claim|mem|slab" MemTotal: 1017448 kB MemFree: 97304 kB MemAvailable: 316264 kB Active: 317736 kB Inactive: 371132 kB Active(anon): 209564 kB Inactive(anon): 244268 kB Active(file): 108172 kB Inactive(file): 126864 kB Dirty: 324 kB Shmem: 3444 kB Slab: 192720 kB SReclaimable: 152860 kB SUnreclaim: 39860 kB
- anon(Anomymous) - プロセスが確保したメモリ。ディスクに退避(スワップ)されることがある。
- file(File-backed) - OSがディスクキャッシュとして確保したメモリ。(Dirtyなもの以外は)もともとディスク上に存在するデータ。
Shmem - tmpfsとして使用されているメモリ。tmpfsはメモリ上にのみ存在するが、スワップの対象となりディスクに書き出される。
Active/Inactive - LRU的に、最近使用されたメモリか、利用されていないメモリか。
Dirty - ディスクに書き込まれていないFile-backedなバッファ。syncでディスクに書き込むと0になり、Cleanなバッファとなる。Cleanなバッファは、
echo 1 > /proc/sys/vm/drop_caches
で解放される。
$ cat /proc/meminfo | grep Dirty Dirty: 1032 kB $ sync $ cat /proc/meminfo | grep Dirty Dirty: 0 kB
Slab - SReclaimable + SUnreclaim
- SReclaimable - 解放可能なSlab。
echo 2 > /proc/sys/vm/drop_caches
で解放される。 - SUnreclaim - 解放不可能なSlab。
- SReclaimable - 解放可能なSlab。
echo 2 > /proc/sys/vm/drop_caches
の直後でもSReclaimableは0にならないが、slabtop
で確認するとdentryやinodeがSlabを使用しているのが分かる。バックグラウンドプロセスやカーネルの処理が動いているため、すぐにSlabの割り当てが行われているのだろう。
$ cat /proc/meminfo | egrep -i "slab|claim" Slab: 210504 kB SReclaimable: 170240 kB SUnreclaim: 40264 kB $ echo 2 > /proc/sys/vm/drop_caches $ cat /proc/meminfo | egrep -i "slab|claim" Slab: 79436 kB SReclaimable: 45460 kB SUnreclaim: 33976 kB $ slabtop -o -sc | head -n 12 Active / Total Objects (% used) : 579600 / 661586 (87.6%) Active / Total Slabs (% used) : 11754 / 11754 (100.0%) Active / Total Caches (% used) : 74 / 100 (74.0%) Active / Total Size (% used) : 51588.87K / 78695.04K (65.6%) Minimum / Average / Maximum Object : 0.01K / 0.12K / 8.00K OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME 29368 8839 30% 0.99K 1894 16 30304K ext4_inode_cache 9674 9378 96% 0.55K 691 14 5528K inode_cache 63189 20416 32% 0.08K 1239 51 4956K Acpi-State 21210 12372 58% 0.19K 1010 21 4040K dentry 118528 118528 100% 0.03K 926 128 3704K kmalloc-32 $ slabtop --help ... The following are valid sort criteria: c: sort by cache size ...
$ sudo cat /proc/vmallocinfo | grep ioremap 0xffffc90000000000-0xffffc90000004000 16384 acpi_os_map_iomem+0xf7/0x150 phys=3fff0000 ioremap 0xffffc900001f8000-0xffffc900001fb000 12288 pci_iomap+0x55/0xb0 phys=f0806000 ioremap 0xffffc900001fc000-0xffffc900001fe000 8192 usb_hcd_pci_probe+0x14d/0x550 phys=f0804000 ioremap 0xffffc90000280000-0xffffc900002a1000 135168 pci_ioremap_bar+0x46/0x80 phys=f0000000 ioremap 0xffffc90000300000-0xffffc90000701000 4198400 vgdrvLinuxProbePci+0x9c/0x210 [vboxguest] phys=f0400000 ioremap $ sudo cat /proc/vmallocinfo | grep ioremap | awk '{print $2}' | paste -s -d '+' 16384+12288+8192+135168+4198400 $ sudo cat /proc/vmallocinfo | grep ioremap | awk '{print $2}' | paste -s -d '+' | bc 4370432
free
- /proc/meminfoの内容を元に、メモリの使用量や残量を示してくれる。なおx86(32bit)では、
l
オプションはカーネルが使用するLowメモリと、プロセスが使用するHighメモリを分けて表示してくれる。なおカーネルが使用するLowメモリは、物理アドレスの1MB〜893MBまで、また各プロセスの論理アドレスの3GB〜4GBの領域を占める。x86_64(64bit)では、LowメモリやHighメモリといった区別が無いため、l
オプションで表示する場合は全てLowメモリとして表示し、Highメモリは常に0
として表示されるようである。
$ free -l total used free shared buffers cached Mem: 1017448 949096 68352 4004 88984 131476 Low: 1017448 949096 68352 High: 0 0 0 -/+ buffers/cache: 728636 288812 Swap: 839676 45872 793804
Slab
メモリの割り当てや解放を繰り返して毎回初期化するよりも、カーネルはOSにメモリを戻さずに(解放せずに)初期化した状態のまま保持し続けておき、 必要になったらそこから再利用することでパーフォーマンスが上がる。Slabはオブジェクトキャッシュとも呼ばれる。
小さい単位のメモリ割り当て・解放を、その都度メモリから自由に行うとフラグメントの問題を引き起こしやすくなるが、Slabとして保持しておいて再利用することで、少なくともスラブを利用している限りにおいてはフラグメントの問題は起こらない。
SlabのAPI
- kmem_cache_create - Slabを確保する
- kmem_cache_alloc - Slabから実際に使うオブジェクトへメモリを払い出す
- kmem_cache_free
Slabは単一のオブジェクトを連続したメモリ上に複数格納する。
- Slabがemptyになると、リープ(OSに戻す)対象となる。
- Cache Coloring - Slabのオブジェクトはページに沿って配置されているため、CPUの特定のキャシュラインに集中してしまう。別のオブジェクトがCPUの別のキャッシュラインに載るように、Slabごとに少しフラグメントを持たせてアドレスをずらす。
Slabが大量消費されていることでメモリを圧迫する問題(問題?)
dentryやinodeはSlabのヘビーユーザであり、それによってSlabが大量に消費されていることがある。/proc/sys/vm/drop_cachesに2 or 3を書き込むことで、Slab(SReclaimable)は解放されるが、Slabを完全に解放してしまうとLinuxとしての性能は格段に落ちる。
そもそもメモリをたくさん消費しているのはリソースを効率良く使用できている状態とも言えるので、問題なのはそれによってスワッピングが発生しているか否かであり、それは
vmstat
コマンドのsi,soの項目を確認することで分かる。SlabもLRUによって管理されているはずなので、基本的にはそれにまかせるのが得策なのではないだろうか。 なお、vmstat
で最初に表示される結果は起動時からの平均値であり、単位時間の結果を確認する場合には二つ目以降を確認する。
$ vmstat procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu---- r b swpd free buff cache si so bi bo in cs us sy id wa 1 1 51596 75360 71636 172804 1 10 337 19 142 462 1 2 96 1 $ man vmstat ... Swap si: Amount of memory swapped in from disk (/s). so: Amount of memory swapped to disk (/s). ...
- Slabのページキャッシュに対する比率を制御するには、/proc/sys/vm/vfs_cache_pressure を使う。100を基準として、Slabの比率を増やしたり減らしたりできるらしい。その他、/proc/sys/vmで管理されているパラメータについてはこちら → https://www.kernel.org/doc/Documentation/sysctl/vm.txt
Slabの肥大化で実際にスワップが発生した問題とその解決方法
- いきなり上記で書いた内容と矛盾するが、Slabの肥大化によってスワップが発生してしまった事例がある。つまり、メモリ圧迫時にはLRUに基づくSlabの解放を期待したがうまく動いてくれなかった、ということになる。その原因として考えられるのは、
dentry cacheを解放するスピードが間に合わないのか
、とある。解決方法としては、tmpfsを使うことによりSlabの肥大化を回避できる、ということで、その理由としてはtmpfsがLinuxのVFSを使わないからでしょうか
とのことだが、tmpfsではそもそもdentry cacheを生成しないのだろうか。この事例から「一時ファイルはtmpfsにおいて処理する」は鉄則だろう。
- negative dentry - 存在しないパスを開くと、それについてもnegativeなdentry cacheとしてキャッシュする。dentry cacheにヒットしない場合、ファイルシステムに問い合わせることでディスクI/Oが発生するが、negative entryがあればその必要が無いのでディスクI/Oを減らす効果が期待できる。negative dentryは
sar -v
コマンドの結果のdentunusd
が示す値に含まれているらしい。なお、negative cacheはDNSでも不要なクエリを発生させないために使われることがある。
$ sar -v | tail -n +3 | head -n4 12:10:01 AM dentunusd file-nr inode-nr pty-nr 12:20:01 AM 80492 4832 75177 1 12:30:01 AM 80506 4832 75184 1 12:40:01 AM 80495 4800 75166 1 $ man sar ... dentunusd Number of unused cache entries in the directory cache. ...
ページキャッシュを効率よく管理するためのアプローチ
- cachectld - posix_fadviseとPOSIX_FADV_DONTNEEDをディレクトリ単位で適用することにより、必要なページキャッシュは残しつつ、不要なページキャッシュだけをディレクトリ単位で解放する。
ページキャッシュが不要ならO_DIRECTを使うアプローチは?
openシステムコールにO_DIRECTフラグを渡すと、ファイルへの書き込みや読み込みをVFSを介さずにファイルシステムと直接行うため、メモリにページキャッシュを残すことが無い。これは、再利用することが無いと分かっている大きなファイルを開くときには、無用にメモリを使用することが無く、他のAnonymousやFile-backedなメモリに対して優しいと言える。ただし書き込みの際は、バッファへの書き込みが行われず、プロセスがディスクへの書き込み待ちで長い時間ブロックされることになるため注意が必要。
書き込みはページキャッシュとしてメモリに蓄えられた後で非同期に(flusherスレッドによってデフォルト5秒間隔で)ディスクへ書き込まれるので、通常はプロセスが書き込みでブロックされることはない。一時的にメモリを消費してしまうものの、ディスクへ書き込んだ直後に該当のページキャッシュを解放するような仕組みがあっても良さそう。
ramfs
- ramfsはtmpfsのようにメモリ上に作成されるファイルシステムだが、ramfsの方は物理ディスクがないにも関わらず、File-backedとして登録されるため、ディスクにスワップされることがない。
プロのための Linuxシステム構築・運用技術 (Software Design plus)
- 作者: 中井悦司
- 出版社/メーカー: 技術評論社
- 発売日: 2010/12/22
- メディア: 大型本
- 購入: 21人 クリック: 411回
- この商品を含むブログ (38件) を見る