とある python ライブラリが mmap(2) を使ってファイルを MAP_SHARED | MAP_POPULATE
flags 付きでメモリに読み込んでいたので、その時 Linux メモリ管理系ツールではどういう挙動になるかチラ裏。
mmap お試しコード
#include <fcntl.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
int main() {
int fd = open("abc.txt", O_RDONLY, (int)0400);
off_t size = lseek(fd, 0, SEEK_END);
printf("start\n");
char* addr = mmap(NULL, size, PROT_READ, MAP_SHARED | MAP_POPULATE, fd, 0);
printf("end\n");
sleep(100);
return 0;
}
Linux 環境で
$ gcc mmap-sample.c
$ dd if=/dev/zero of=abc.txt bs=5M count=1024
$ ./a.out
start
[しばし待つ]
end
$ ./a.out
start
[すぐ終わる]
end
free
mmap でファイルをメモリにマッピングしたわけだが、buff/cache のサイズが 5G 増えた。
$ free -m
total used free shared buff/cache available
Mem: 61399 348 55745 0 5305 60453
Swap: 0 0 0
2回目の実行が早かったのは buff/cache にすでにメモリが載っていたから。cache を消したい場合は次のようにして drop cache する。
$ echo 3 | sudo tee /proc/sys/vm/drop_caches
ps
RSSに5GBが含まれている。つまり ps の RSS は buff/cache として確保したメモリも含んでいる。
cache から追い出されたら VSZ はそのままで、RSS は小さくなるはず(未確認
$ ps au
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
sonots 15592 4.7 8.3 5247396 5242312 pts/12 S+ 18:08 0:00 ./a.out
top
で見た時の RES 列も同様に 5GB を含んでいる
cgroup
cgroup 内の現在のメモリ使用量を示す memory.usage_in_bytes
について。
- cgroup A で mmap
- そのあと cgroup B で mmap
したとする。k8s でいうと、同一ノードで pod A と pod B が立ち上がったような状況。
1 の cgroup A の memory.usage_in_bytes
は mmap したメモリ使用量を含む。
2 の cgroup B では、直前に cgroup A で mmap しておりノードの buffer/cache にキャッシュが載っているのでそれが再利用される。cgroup B の memory.usage_in_bytes
には mmap したメモリ使用量が含まれない。
First touch の原則というものがあるらしい。
Shared pages are accounted on the basis of the first touch approach. The cgroup that first touches a page is accounted for the page.
ref. https://serverfault.com/questions/903432/page-cache-usage-listed-in-cgroups-memory-stat-file
total memory を超えて別ファイルを mmap しようとした場合
buffer/cache から前の mmap が追い出されて、新しいファイルが buffer/cache に載る。
つまり
mmap が使われていると、2回目から起動が早くなったり、メモリを大量に確保しているのに cgroup の limit に引っかからなかったりするということ。