為什麼手工DROP_CACHES之後CACHE值並未減少?

碼農世界 發佈 2023-01-14T09:14:26.675808+00:00

在Linux系統上查看內存使用狀況最常用的命令是」free」,其中buffers和cache通常被認為是可以回收的:12345$ freetotal used free shared buffers cachedMem: 32764716 1067548 31697168 158

在Linux系統上查看內存使用狀況最常用的命令是」free」,其中buffers和cache通常被認為是可以回收的:

1

2

3

4

5

$ free

total used free shared buffers cached

Mem: 32764716 1067548 31697168 158332 12 593096

-/+ buffers/cache: 474440 32290276

Swap: 2103292 0 2103292

當內存緊張的時候,有一個常用的手段就是使用下面的命令來手工回收cache:

1

$ echo 1 > /proc/sys/vm/drop_caches

註:drop_caches接受以下三種值:

  • To free pagecache:
    echo 1 > /proc/sys/vm/drop_caches
  • To free reclaimable slab objects (includes dentries and inodes):
    echo 2 > /proc/sys/vm/drop_caches
  • To free Slab objects and pagecache:
    echo 3 > /proc/sys/vm/drop_caches

當我們考慮有多少cache可供回收的時候,首先要知道的是:不同版本的」free」命令計算cache值的算法不同,據不完全統計舉例如下:

  1. 版本:procps-3.2.8-36
    cache值等於/proc/meminfo中的」Cached」;
  2. 版本:procps-3.3.9-10.1
    cache值等於/proc/meminfo的 [Cached + SReclaimable];
  3. 版本:procps-ng-3.3.10-3
    cache值等於/proc/meminfo的 [Cached + Slab]。

註:
/proc/meminfo中的」Cached」表示page cache所占用的內存大小;
「Slab」表示內核Slab所占用的內存大小,slab有的可回收有的不可回收,其中可回收的通過」SReclaimable」表示,不可回收的通過」SUnreclaim」表示。
所以,對上述第2、3版本的」free」命令,」echo 1 > /proc/sys/vm/drop_caches」對其中的SReclaimable或Slab部分是不起作用的。

即便僅考慮page cache (對應於 /proc/meminfo 的」Cached」),也並不是所有的頁面都可以回收的:

首先,drop_caches只回收clean pages,不回收dirty pages,參見https://www.kernel.org/doc/Documentation/sysctl/vm.txt
所以如果想回收更多的cache,應該在drop_caches之前先執行」sync」命令,把dirty pages變成clean pages。

其次,即使提前執行了sync命令,drop_cache操作也不可能把cache值降到0,甚至有時候cache值幾乎沒有下降,這是為什麼呢?因為page cache中包含的tmpfs和共享內存是不能通過drop_caches回收的。

Page cache用於緩存文件里的數據,不僅包括普通的磁碟文件,還包括了tmpfs文件,tmpfs文件系統是將一部分內存空間模擬成文件系統,由於背後並沒有對應著磁碟,無法進行paging(換頁),只能進行swapping(交換),在執行drop_cache操作的時候tmpfs對應的page cache並不會回收。

我們通過實驗來觀察tmpfs文件對free命令的影響:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

掛載一個3G大小的tmpfs:

$ mount -t tmpfs -o size=3G none /mytmpfs/

此時free命令看到的內存使用狀況:

$ free

total used free shared buffers cached

Mem: 65942736 2343336 63599400 9492 8952 92848

-/+ buffers/cache: 2241536 63701200

Swap: 33038332 0 33038332

在tmpfs上新建一個2G大小的文件,free命令看到"cached"增加了2GB,注意"shared"也增加了2GB:

$ dd if=/dev/zero of=/mytmpfs/testfile bs=1G count=2

2+0 records in

2+0 records out

2147483648 bytes (2.1 GB) copied, 2.42736 s, 885 MB/s

$ free

total used free shared buffers cached

Mem: 65942736 4423404 61519332 2106644 9088 2190064

-/+ buffers/cache: 2224252 63718484

Swap: 33038332 0 33038332

執行drop_caches,再觀察free命令的"cached"值,發現剛才增加的2GB並未被回收:

$ sync

$ sudo sh -c 'echo 1 > /proc/sys/vm/drop_caches'

$ free

total used free shared buffers cached

Mem: 65942736 4430388 61512348 2106644 5284 2183096

-/+ buffers/cache: 2242008 63700728

Swap: 33038332 0 33038332

最後刪除tmpfs上的文件,free命令看到"Cached"和"Shared"同時減少2GB:

$ rm /mytmpfs/testfile

$ free

total used free shared buffers cached

Mem: 65942736 2324096 63618640 9784 6228 87412

-/+ buffers/cache: 2230456 63712280

Swap: 33038332 0 33038332

結論:
tmpfs占用的page cache是不能通過drop_caches操作回收的,tmpfs占用的page cache同時也算進了」shared」中,也就是說,被視為共享內存。

Linux kernel利用tmpfs實現共享內存(shared memory),參見:
https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt
所以共享內存也和tmpfs一樣,屬於page cache,但又不能被drop_caches回收。這裡所說的共享內存包括:

  • SysV shared memory
    是通過shmget申請的共享內存,用」ipcs -m」或」cat /proc/sysvipc/shm」查看;
  • POSIX shared memory
    是通過shm_open申請的共享內存,用」ls /dev/shm」查看;
  • tmpfs文件系統和devtmpfs文件系統
    所有tmpfs文件系統占用的空間都計入共享內存,devtmpfs是/dev文件系統的類型,/dev/下所有的文件占用的空間也屬於共享內存。可以用ls和du命令查看。如果文件沒有關閉的情況下被刪除,空間仍然不會釋放,可以用 「lsof -a +L1 /<mount_point>」 命令查看。
  • shared anonymous mmap
    通過mmap(…MAP_ANONYMOUS|MAP_SHARED…)申請的內存,可以用」pmap -x」或者」cat /proc/<PID>/maps」查看;
    註:mmap調用參數如果不是MAP_ANONYMOUS|MAP_SHARED,則不屬於tmpfs,比如MAP_ANONYMOUS|MAP_PRIVATE根本不屬於page cache而是屬於AnonPages,MAP_SHARED屬於普通文件,對應的page cache可以回寫硬碟並回收。

我們通過一個實驗來觀察共享內存對free命令的影響。以下程序通過shared anonymous mmap方式申請256MB大小的內存:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

#include <sys/mman.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#define MAP_SIZE 268435456

int main()

{

char *addr;

ssize_t s;

addr = mmap(NULL, MAP_SIZE, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED, -1, 0);

if (addr == MAP_FAILED) {

fprintf(stderr, "mmap failed\n");

exit(EXIT_FAILURE);

}

memset(addr, 9, MAP_SIZE);

printf("mmap done, memset done, check free output. Press any key to exit...\n");

getchar();

exit(EXIT_SUCCESS);

}

實驗過程如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

先回收cache,設置初始狀態:

$ sync; sudo sh -c 'echo 1 > /proc/sys/vm/drop_caches'; free

total used free shared buffers cached

Mem: 65942736 2401684 63541052 9576 3648 54820

-/+ buffers/cache: 2343216 63599520

Swap: 33038332 0 33038332

然後執行我們的測試程序,申請256MB共享內存:

$ ./mmap_test

mmap done, memset done, check free output. Press any key to exit...

在另一個窗口裡觀察,看到cache值增加了256MB,注意"shared"也同時增加了256MB:

$ free

total used free shared buffers cached

Mem: 65942736 2652796 63289940 271720 6380 321760

-/+ buffers/cache: 2324656 63618080

Swap: 33038332 0 33038332

執行drop_caches,發現剛才新增的256MBcache值並未被回收:

$ sync; sudo sh -c 'echo 1 > /proc/sys/vm/drop_caches'; free

total used free shared buffers cached

Mem: 65942736 2666452 63276284 271720 3628 316532

-/+ buffers/cache: 2346292 63596444

Swap: 33038332 0 33038332

退出mmap_test程序,再看cache值就減少了256MB,注意"shared"也同時減少256MB:

$ free

total used free shared buffers cached

Mem: 65942736 2400268 63542468 9576 4796 59148

-/+ buffers/cache: 2336324 63606412

Swap: 33038332 0 33038332

結論:cache值包含了共享內存的大小,然而drop_caches是不能回收它的。
註:上例是shared anonymous mmap,如果是SysV shared memory或POSIX shared memory,結果是一樣的。

總結:為什麼drop_caches操作不能回收所有的page cache?因為

  • dirty pages不能回收;
  • 共享內存和tmpfs不能回收(注意觀察free命令顯示的shared值);
  • 不同版本的free命令有不同的計算cache值的方法,有的包含了slab或SReclaimable,」echo 1 > /proc/sys/vm/drop_caches」是不能回收slab的,」echo 3 > /proc/sys/vm/drop_caches」也只是回收slab中的SReclaimable部分。
關鍵字: