Linux

mmap と free と ps と cgroup

とある python ライブラリが mmap(2) を使ってファイルを MAP_SHARED | MAP_POPULATE flags 付きでメモリに読み込んでいたので、その時 Linux メモリ管理系ツールではどういう挙動になるかチラ裏。


mmap お試しコード


mmap-sample.c

#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 について。


  1. cgroup A で mmap

  2. そのあと 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 に引っかからなかったりするということ。