当我的 Linux 系统接近分页时(即,在我的情况下,16GB 内存几乎已满,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;
}
第二段代码的作用与第一段代码完全相同,只是它sleep(1);
在 后面有一个右侧printf
(我不会重复整个代码)。当系统处于分页边缘时,将使用此方法,以使其以“温和”的方式交换页面,即通过缓慢请求分配新的内存块(这样系统当然应该能够交换页面)并跟上新的要求)。
因此,编译完这两段代码后,我们分别调用各自的exe fasteater和sloweater,让我们这样做:
1)启动你最喜欢的图形用户界面(当然不是绝对必要的)
2)启动一些内存/交换表(例如watch -n 1 free
)
3) 启动多个实例,fasteater x y
其中 x 的数量级为千兆字节,y 的数量级为兆字节。这样做直到几乎填满公羊。
4) 再次启动 的一个实例sloweater x y
,其中 x 的数量级为 GB,y 的数量级为 MB。
步骤 4)之后应该发生的情况(对于我的系统来说总是发生)是,在耗尽内存后,系统将完全锁定。 gui 被锁定,sshd 被锁定等等。但是,不是永远!在 Sloweater 完成其分配请求后,系统将恢复正常(在锁定几分钟后,而不是几秒......),出现以下情况:
a) 内存即将满
b) 交换区也差不多满了(记住,一开始它是空的)
c) 没有 oom Killer 干预。
请注意,交换分区位于 SSD 上。因此,系统似乎无法逐渐将页面从 ram 移动到交换区(可能是从刚刚休眠的 fasteater 中),以便为 Sloweater 的慢速(只有几兆字节)请求腾出空间。
现在,如果我错了,有人会纠正我,但这似乎不是现代系统在这种情况下应该表现的方式。当不支持分页并且虚拟内存系统只是交换出某个进程的整个内存空间而不是几个页面时,它的行为似乎就像旧系统(waaaaay)一样。
有人也可以测试这个吗?也许有人也有 BSD 系统。
更新1
我听从了建议马克·普洛特尼克下面的评论中,我vmstat 1 >out
在进行分页测试之前就开始了。您可以看到下面的结果(我剪切了填充内存的整个初始部分,而没有涉及交换):
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
正如您所看到的,一旦涉及交换,就会立即产生 15916880 KB 的大量交换,我猜这会持续整个系统冻结期间。所有这一切显然都是由每秒只请求 10MB 的进程(慢速器)引起的。
更新2:我快速安装了 FreeBSD,并重复了与 Linux 相同的分配方案......并且一切顺利。 FreeBSD 逐渐交换页面,而慢速器则分配所有 10MB 内存块。没有任何问题......这里发生了什么?!
更新3:我提交了漏洞与内核错误跟踪器。它似乎引起了一些关注,所以...手指交叉...
答案1
这正是冲击保护存在于.
它不断监视交换状态,当某些东西意外开始占用大量 RAM 时,会暂时冻结 RAM 贪婪进程,以便内核有时间交换一些内存,而不会使整个系统失去响应。
答案2
您只是分配内存 - 实际上并没有在其中放入任何内容。 “正常”程序会分配一个块,然后开始使用它。分配与内存使用不同。