这是 Linux 分页的行为方式吗?

Joh*_*gon 26 kernel swap virtual-memory

当我的 Linux 系统接近分页时(即,在我的情况下,16GB ram 几乎已满,16GB 交换完全空),如果新进程 X 尝试分配一些内存,系统将完全锁定。也就是说,直到不成比例的页面数量(与 X 的内存分配请求的总大小和速率)被换出。请注意,不仅 gui 变得完全没有响应,而且甚至像 sshd 这样的基本服务也完全被阻止。

这是我用来以更“科学”的方式触发这种行为的两段代码(诚然粗糙)。第一个从命令行获取两个数字 x,y 并继续分配和初始化多个 y 字节块,直到分配的总字节数超过 x 为止。然后无限期地睡觉。这将用于使系统处于寻呼的边缘。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char** argv) {
   long int max = -1;
   int mb = 0;
   long int size = 0;
   long int total = 0;
   char* buffer;

   if(argc > 1)
     {
       max = atol(argv[1]);
       size = atol(argv[2]);
     }
   printf("Max: %lu bytes\n", max);
   while((buffer=malloc(size)) != NULL && total < max) {
       memset(buffer, 0, size);
       mb++;
       total=mb*size;
       printf("Allocated %lu bytes\n", total);       
   }      
   sleep(3000000);
   return 0;
}
Run Code Online (Sandbox Code Playgroud)

第二段代码与第一段完全一样,只是它sleep(1);后面有一个右边printf(我不打算重复整个代码)。当系统处于分页边缘时,将使用此方法以“温和”的方式换出页面,即通过缓慢请求分配新的内存块(这样系统当然应该能够换出页面并跟上新的要求)。

所以,编译好这两段代码后,让我们分别调用各自的exes fasteater 和sloweater,让我们这样做:

1)启动你最喜欢的gui(当然不是绝对必要的)

2)启动一些内存/交换表(例如watch -n 1 free

3) 启动多个实例,fasteater x y其中 x 为千兆字节,y 为兆字节。这样做直到你几乎填满公羊。

4)sloweater x y再次启动 的一个实例,其中 x 的数量级为千兆字节,而 y 的数量级为兆字节。

在第 4 步之后,应该发生的事情(对于我的系统来说总是发生)是,在用完 ram 之后,系统将完全锁定。gui 已锁定 sshd 已锁定等。但是,不是永远!在sloweater完成其分配请求后,系统将在这种情况下恢复生机(锁定几分钟后,而不是几秒钟......):

a) ram 快满了

b) swap 也差不多满了(记住,一开始它是空的)

c) 没有 oom 杀手干预。

请注意,交换分区位于 SSD 上。因此,系统似乎无法逐渐将页面从 ram 移动到交换区(大概是从刚刚休眠的快食者)为慢食者的慢速(仅几兆字节)请求腾出空间。

现在,如果我错了,有人会纠正我,但这似乎不是现代系统在这种情况下应该表现的方式。当不支持分页并且虚拟内存系统只是换出某些进程的整个内存空间而不是几页时,它的行为似乎与旧系统(waaaaay back)相似。

有人也可以测试一下吗?也许有人也有 BSD 系统。

更新 1 我在下面的评论中遵循了Mark Plotnick的建议,并vmstat 1 >out在继续分页测试之前开始了。你可以看到下面的结果(我切割了整个初始部分,其中 ram 在没有交换参与的情况下被填充):

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
0  0   6144 160792      8 272868    0    0     0     0  281 1839  1  0 99  0  0
0  0   6144 177844      8 246096    0    0     0     0  425 2300  1  1 99  0  0
0  0   6144 168528      8 246112    0    0    16     0  293 1939  1  0 99  0  0
0  0   6144 158320      8 246116    0    0     0     0  261 1245  0  0 100  0  0
2  0  10752 161624      8 229024    0 4820 17148  4820  845 3656  1  2 97  0  0
2  0  10752 157300      8 228096    0    0 88348     0 2114 8902  0  5 94  1  0
0  0  10752 176108      8 200052    0    0 108312     0 2466 9772  1  5 91  3  0
0  0  10752 170040      8 196780    0    0 17380     0  507 1895  0  1 99  0  0
0 10  10752 160436      8 191244    0    0 346872    20 4184 17274  1  9 64 26  0
0 29 12033856 152888      8 116696 5992 15916880 1074132 15925816 819374 2473643  0 94  0  6  0
3 21 12031552 295644      8 136536 1188    0 11348     0 1362 3913  0  1 10 89  0
0 11 12030528 394072      8 151000 2016    0 17304     0  907 2867  0  1 13 86  0
0 11 12030016 485252      8 158528  708    0  7472     0  566 1680  0  1 23 77  0
0 11 12029248 605820      8 159608  900    0  2024     0  371 1289  0  0 31 69  0
0 11 12028992 725344      8 160472 1076    0  1204     0  387 1381  0  1 33 66  0
0 12 12028480 842276      8 162056  724    0  3112     0  357 1142  0  1 38 61  0
0 13 12027968 937828      8 162652  776    0  1312     0  363 1191  0  1 31 68  0
0  9 12027456 1085672      8 163260  656    0  1520     0  439 1497  0  0 30 69  0
0 10 12027200 1207624      8 163684  728    0   992     0  411 1268  0  0 42 58  0
0  9 12026688 1331492      8 164740  600    0  1732     0  392 1203  0  0 36 64  0
0  9 12026432 1458312      8 166020  628    0  1644     0  366 1176  0  0 33 66  0
Run Code Online (Sandbox Code Playgroud)

如您所见,一旦涉及交换,就会同时发生 15916880 KB 的大量交换,我猜,这会持续整个系统冻结期间。而这一切显然是由一个每秒只要求 10MB 的进程(慢食者)引起的。

更新 2:我快速安装了 FreeBSD,并重复了与 Linux 相同的分配方案......它应该是顺利的。FreeBSD 逐渐交换页面,而慢食者分配了所有 10MB 的内存块。没有任何问题......WTF在这里进行?!

更新 3:我向内核错误跟踪器提交了一个错误。似乎受到了一些关注,所以……手指交叉……

Jer*_*den -3

您只是分配内存 - 实际上并没有在其中放入任何内容。“正常”程序会分配一个块,然后开始使用它。分配与内存使用不同。

  • 欢迎在 Unix StackExchange 上发帖。它确实将数据放入其中,该数据恰好为零。请参阅 memset()。一旦写入虚拟页,Linux 内核就会提供 RAM 的物理页;它不查看写入的具体值。 (3认同)