tmpfs は本当に容量が動的なのか
Linux には tmpfs という便利なファイルシステムがあります。
$ mount -t tmpfs -o size=64m tmpfs /dev/shm $ mount -t tmpfs -o size=64m /dev/shm /var/tmp
とすると、/var/tmp がディスク上ではなくメモリ上に作られたファイルシステムとして mount されます。なので、/var/tmp は I/O 時にディスクI/Oが一切発生しない高速なディスクとして使えると。いわゆる RAM ディスク。(もちろんサーバーの電源を落とすと保存したファイルは消えます。)
この tmpfs はなかなかに便利で、キャッシュとかそういうものでディスクにおいてたものここ置くと、ディスク I/O がカットできて超高速になります。はてなでは MySQL のスレーブの MyISAM のファイルを tmpfs において、オンメモリなデータベースを作って使ったりということも結構してます。(スレーブなら万が一サーバーが落ちてデータがなくなってもすぐ復帰できるので OK と。)
Linux で RAM ディスクを実現するためのファイルシステムは tmpfs 以外にも ramfs というのがあります。問題はこの tmpfs と ramfs の違い。
Google で tmpfs で検索すると、容量が動的に変化するRAMディスクを使うにはという記事がヒットします。で、ここには
仮想メモリベースのファイルシステムであるtmpfsを使用すれば、必要なサイズに応じてRAMディスクの容量が動的に変化するため、メモリを効率よく使用できる。
という記述があり、容量固定の ramfs に対して tmpfs は容量が伸縮するみたいに読めます。そういう風に理解している人は多いと思います。僕もそうです。
- 2GB の物理メモリを積んだマシンがあって、ramfs で 1.5GB 確保すると、OS が使えるのは残りの 500MB。そうなるのが ramfs。
- 2GB の物理メモリを積んだマシンがあって、tmpfs で 1.5GB 確保しても、OS が使えるのは 2GB。tmpfs に 700MB 書き込むと、OS が使えるのは残り 1.3GB になる。
という風に理解していました。
が、どうもその挙動がそうなのかどうなのかが怪しい。Linux に添付されてるドキュメントが JF にあったのでちょっと引用します。
If you compare it to ramfs (which was the template to create tmpfs) you gain swapping and limit checking. Another similar thing is the RAM disk (/dev/ram*), which simulates a fixed size hard disk in physical RAM, where you have to create an ordinary filesystem on top. Ramdisks cannot swap and you do not have the possibility to resize them.
(tmpfs を作成するテンプレートであった) ramfs と比較すると、スワップと、制限チェックに利点があります。もうひとつ別の良く似たものは、RAM ディスク( /dev/ram ) で、物理RAM 内にサイズを固定したハードディスクを、シミュレートしますが、最上位に、通常のファイルシステムを作成しなければなりません。RAM ディスクはスワップできませんし、サイズの変更もできません。
確かに tmpfs の方が ramfs よりも動的な性質を持っているとはあるのですが、それはスワップできるという点であって、「tmpfs で作成した領域の空いてるところは OS 側が使える」みたいなことは書いてありませんでした。
また、
tmpfsとramfsの機能は似ていますが、tmpfsはページアウトされるのに対し、ramfsはページアウトされない点が異なります。
とオラクル通信の記事でも tmpfs と ramfs の違いはページアウトの有無(スワップするかしないか)だと言及されてます。
- tmpfs は OS がメモリが足りなくなったときにスワップして、OS 側にメモリを確保させる機能がある
ということは分かるのですが、tmpfs として確保した領域のうち空いてる箇所は果たして
- tmpfs として確保した領域のうち使用されてないものは OS がアプリケーションに割り当てる
- tmpfs として確保されたものはあくまでファイルシステムとして扱われ、OS がアプリケーションに割り当てることはない
のどちらなのかがはっきりしません。
そもそもこの疑問を持つきっかけがちょうど昨日ありまして。とあるサーバーの tmpfs の領域を拡張したのですが、スワップが発生したので tmpfs の領域を減らしてみたところスワップがなくなった、というケースが実際に発生したんです。この状況を見るに、tmpfs の空き領域は OS がアプリケーションに割り当てられない、という風に見れてしまいます。
で、もう少し検証するべく id:danjou に実験してもらいました。物理メモリ1GBを積んでるマシンに1GBの tmpfs 領域を確保して、どういうことになるか。
$ sudo mount -t tmpfs -o remount,size=1024m tmpfs /dev/shm $ sudo mount -t tmpfs -o size=1024m /dev/shm /home/danjou/tmp
こんな感じでtmpfsを作って、その時点のdfとfree。
$ free total used free shared buffers cached Mem: 1002952 97600 905352 0 6500 46828 -/+ buffers/cache: 44272 958680 Swap: 2031608 0 2031608
メモリは 1GB でスワップは一切使っていない。で、メモリの空き容量(free)は 900 MB ある。つまり、tmpfs で確保してる領域も「空きメモリ」として換算されていると見れます。
$ df Filesystem 1K-ブロック 使用 使用可 使用% マウント位置 /dev/mapper/VolGroup00-LogVol00 35740376 1534852 32360688 5% / /dev/hda1 101086 9950 85917 11% /boot /dev/shm 1048576 0 1048576 0% /dev/shm /dev/shm 1048576 4 1048572 1% /home/danjou/tmp
これは df の結果。tmpfs で mount した領域はもちろんほとんど使用してない。(ちょっとしたファイルを置いてあってそれが 4bytes だった様子)
この状態から dd で tmpfs にでかいファイルを作っていって、スワップの発生状況を見る。つまり、
$ dd if=/dev/zero of=/home/danjou/tmp/zero count=1024M
これをやりながら vmstat で1秒おきにスワップ I/O を見て行くと。結果はこんな感じ。
procs -----------memory---------- ---swap-- -----io---- --system-- ----cpu---- r b swpd free buff cache si so bi bo in cs us sy id wa 0 0 0 903508 6636 47368 0 0 0 0 1008 25 0 0 100 0 1 0 0 795600 6636 155076 0 0 0 0 1033 36 12 49 38 0 1 0 0 615304 6636 335016 0 0 0 0 1011 12 21 79 0 0 1 0 0 435256 6636 514692 0 0 0 0 1027 27 21 79 0 0 1 0 0 255208 6636 694300 0 0 0 0 1008 11 18 82 0 0 1 0 0 75532 6636 873636 0 0 0 0 1023 27 23 77 0 0 0 3 3864 10792 1724 938992 0 0 0 60 1016 84 11 46 0 44 1 4 55980 10876 64 889272 0 768 0 768 1029 149 0 4 0 96 1 1 187092 10976 64 759104 0 896 0 896 1011 170 1 5 0 94 0 3 430128 10872 64 517548 0 1460 0 1460 1033 193 0 6 0 94 0 4 681312 10680 64 267632 0 1356 0 1356 1014 133 0 5 0 95 0 2 841800 10420 64 110308 0 19636 0 19636 1037 201 1 7 0 92 0 4 854548 11168 64 102244 0 74420 0 74420 1016 125 1 7 0 92 3 2 862244 14152 64 95552 0 50584 0 50584 1047 82 0 65 0 35 2 2 862452 28720 64 95344 0 4744 0 4744 1017 15 0 100 0 0 3 1 863100 10916 64 124964 0 14764 0 14764 1035 51 3 97 0 0 2 1 863676 10916 64 134912 0 10888 0 10888 1024 60 1 99 0 0 2 2 864076 15008 64 146280 0 9948 0 9948 1018 39 2 98 0 0 3 2 864276 10792 64 159844 0 5144 0 5144 1033 58 1 99 0 0 2 2 868280 11388 64 163068 0 61056 0 61056 1025 53 1 99 0 0 2 3 868860 10396 68 172464 0 1728 4 1728 1021 29 1 99 0 0 2 3 869484 14268 76 181948 0 4268 8 4268 1045 36 2 98 0 0 3 2 869968 17876 92 187880 0 956 12 956 1031 25 1 99 0 0 0 2 869968 16388 100 190704 0 4 40 4 1028 29 0 16 0 84 0 2 869968 17132 108 190704 0 0 8 0 1021 23 0 0 0 100
「tmpfs の空き領域はアプリケーションに割り当てられない」という仮定からくと dd しはじめてすぐにスワップしそうなもんですが、そうではなく物理メモリの空き容量が徐々に減っていって、そこで初めてスワップが発生してます。やっぱり、tmpfs の空き領域は純粋に空きメモリとして扱われる、ということでしょうか。
この dd が終わったあとの free と df ですが、
$ df Filesystem 1K-ブロック 使用 使用可 使用% マウント位置 /dev/mapper/VolGroup00-LogVol00 35740376 1534856 32360684 5% / /dev/hda1 101086 9950 85917 11% /boot /dev/shm 1048576 0 1048576 0% /dev/shm /dev/shm 1048576 1048576 0 100% /home/danjou/tmp
tmpfs 領域はきっちり 100% 使っていて、
$ free total used free shared buffers cached Mem: 1002952 977660 25292 0 336 191648 -/+ buffers/cache: 785676 217276 Swap: 2031608 869968 1161640
free の結果スワップが 870MB弱使われた状態になっているのが分かります。おそらくこれは dd で作った、tmpfs に置かれたファイルのうちの多くの部分がスワップ領域に退避させられている、ということでしょう。実際、
$ sudo umount /home/danjou/tmp $ free total used free shared buffers cached Mem: 1002952 351380 651572 0 13628 273932 -/+ buffers/cache: 63820 939132 Swap: 2031608 856 2030752
と umount してから free を観ると、スワップ領域にあったものがごっそりなくなるのが確認できます。また、物理メモリもかなり開放されて空き領域へ移ります。
この実験の結果からは
ということが分かります。僕の理解が考察ってなければ。多分。断言する自信はなし。
ということで、なぜそうドキュメントに書いてないのか、あと、昨日経験したスワップ発生ケースは一体どういうことなのかが説明できない、というのが気持ち悪いまま実験が終わってしまいました。いっそのこと空き容量になってる部分は使えないとなってくれたほうがすっきりしたんだけど。
というわけで自力で調べられるのはここらへんまで。もしカーネルの実装がどうなってるとか、tmpfs 周りに詳しい人とかいたら是非答えを教えてください。