如何控制 Linux 的响应能力、内存和分页

如何控制 Linux 的响应能力、内存和分页

关于溢出的第一个问题 =)... +100 赏金。直到现在我才想起我真正关心的事情:

我对 Linux 桌面的响应能力已经感到厌烦了,例如http://brainstorm.ubuntu.com/item/85/-- 在可用 RAM 较低或磁盘吞吐量较高的情况下,系统速度会降低到爬行;这对于需要良好性能的应用程序来说绝对是糟糕的。此外,用户界面完全没有响应。例如,与 OS X 相比,如果某个应用程序占用资源,可以随时单击选项以强制退出,而在 Linux 中,我甚至无法使用 alt-tab 或切换桌面,甚至无法使用 ctrl-alt-f1 来获取终端 - 好吧,我可以,每次操作只需大约 1-2 分钟。

我使用 gkrellm,这样我就能实时看到情况。通常情况下,内存利用率会变得非常高,或者磁盘吞吐量会急剧增加。

硬件还不错,有 2.6GHz 四核处理器和 4GB 800MHz DDR2 RAM(本来有 6GB,但由于硬件不兼容,无法与旧配置混合搭配)。当我不可避免地获得更多 RAM 时,这个问题可能会消失,但我觉得这不是问题的核心。我甚至在不同的磁盘上有两个交换分区。

我觉得问题有三点:

  • 占用大量内存的失控程序——必须为这些程序制定法律,限制它们的
    • (例如 Chrome 上的标签页,每个标签页大小为 20-50MB,有些标签页甚至会占用数百 MB)
    • (例如,我必须禁用并从 cron 中删除其他程序,如 update-db 和 indexers,因为它们每次运行时都会使系统运行缓慢,等等)
  • 内核中发生了一些可怕的事情,或者发生了某种总线争用,例如高磁盘吞吐量的情况导致整个系统变得缓慢(可能通过分页出重要的程序)
  • 内核没有在资源方面优先考虑 UI 或重要程序,例如内存、分页,甚至处理器利用率

赞同票归于:

因此,我正在寻找一种解决方案,让所有这些程序都消失。具体来说,我正在寻找一种解决方案,使进程按比例减慢速度,而系统和其他程序则保持完全不受影响并且响应时间足够长,可以手动终止某些进程。此外,窗口管理器进程(以及可能影响 UI 响应能力的任何其他进程)在任何情况下都应该响应。

/etc/security/limits.conf我对( )特别感兴趣man limits.conf,但我担心这只会提供每个用户的控制,文件中注释的示例在描述或从哪里开始方面似乎相当晦涩难懂。我希望 a 能limits.conf起作用,但如果它根本不起作用,或者它不是适合我的问题的解决方案,或者不像我试图实现的那样细致,我也不会感到惊讶。每个进程名称limits.conf将是理想的,再次假设 limits.conf 有效。我很乐意尝试人们提供的 limits.conf,以测试它是否有效,尽管目前我对所有解决方案都持开放态度。

了解 OS X 如何保持如此良好的 UI 响应能力也可能会很有用。

我已经对我的/tmp和缓存文件夹进行了调整,使其处于开启状态tmpfs,总体而言磁盘利用率接近于零。

模糊相关的主题:

  • 内存过量使用

我认为答案不起作用:

  • swapoff(这仍然会让占用大量内存的程序逃脱惩罚,如果内存真的很糟糕,系统就会永久冻结——如果有人能建议在交换之前尽早调用 OOM-killer 并针对特定程序,我将不胜感激)
  • echo ?? > /sys/.../swappiness(无明显效果)
  • nice(从未起作用)
  • ionice(从未注意到差异)
  • selinux(程序不兼容似乎是一场噩梦)
  • 实时 Linux,即可以中断内核(不想处理编译和更新自定义内核;如果它已经迁移到存储库中,可能会没问题)
  • *

答案1

听起来您的系统正在进行大量交换。使用vmstat 1可能会显示一些详细信息 - 只需让它在终端窗口中运行,并在速度变慢时切换到它。

我不会将 /tmp 和“缓存”放入 tmpfs,而是使用带有该noatime选项的普通磁盘文件系统。经常使用的数据无论如何都会保留在缓存中,而较旧的数据可以写入磁盘以释放一些 RAM 供应用程序使用。如果 /tmp 和/或缓存变大,这可能会有很大帮助。

答案2

我不是内核开发人员,但我花了数年时间思考这个问题,因为我遇到过很多次这个问题。我实际上为整个情况想出了一个比喻,让我来告诉你。在我的故事中,我假设“交换”之类的东西不存在。无论如何,如今 32 GB RAM 的交换没有多大意义。

想象一下,您所在的社区中,水通过管道连接到每栋建筑,城镇需要管理容量。假设您每秒仅生产 100 单位的水(并且所有未使用的容量都会浪费,因为您没有水箱)。每个家庭(家庭 = 一个小应用程序、一个终端、时钟小部件等)每秒需要 1 单位的水。这一切都很好,因为您的人口大约是 90,所以每个人都有足够的水。

现在市长(=您)决定要开一家大型餐厅(=浏览器)。这家餐厅将容纳多名厨师(=浏览器标签)。每位厨师每秒需要 1 单位的水。您一开始有 10 名厨师,因此整个社区的总用水量为 100 单位水,这仍然很好。

现在有趣的事情开始了:你又雇了一位厨师到你的餐厅,这样总用水量就变成了 101,而你显然没有足够的水。你需要做点什么。

水管理(=内核)有 3 个选项。

1.第一种选择是直接切断最近没有用水的家庭的水源。这很好,但如果被切断的家庭想再次用水,他们将需要再次经历漫长的注册过程。管理层可以切断多个家庭的水源以释放更多的水资源。实际上,他们会切断所有最近没有用水的家庭的水源,从而始终保持一定量的免费水可用。

虽然你的城镇仍在运转,但缺点是发展停滞不前。你大部分时间都在等待水务管理部门恢复服务。

这就是内核对文件支持页面所做的操作。如果您运行大型可执行文件(如 chrome),其文件将被复制到内存中。当内存不足或某些部分最近未访问时,内核可以删除这些部分,因为它无论如何都可以从磁盘重新加载它们。如果过度执行此操作,您的桌面将陷入停顿,因为一切都将等待磁盘 IO。请注意,当您开始进行大量 IO 时,内核还会删除大量最近最少使用的页面。这就是为什么在您复制几个大型文件(如 DVD 映像)后需要很长时间才能切换到后台应用程序的原因。

这对我来说是最烦人的行为,因为我讨厌打嗝,而且你无法控制它。如果能关掉它就好了。我想到的是

sed -i 's/may_unmap = 1/may_unmap = (vm_swappiness >= 0)/' mm/vmscan.c

然后你可以将 vm_swappiness 设置为 -1 来禁用此功能。这在我的小测试中效果很好,但遗憾的是我不是内核开发人员,所以我没有把它发给任何人(显然上面的小修改并不完整)。

2.管理层可以拒绝新厨师的用水请求。这听起来是个好主意。但是有两个缺点。首先,有些公司即使不用水也会要求订购很多水。这样做的一个可能原因是避免在需要额外用水时与水务管理部门沟通的所有开销。他们的用水量会根据一天中的时间而上下波动。例如,在餐厅的案例中,公司中午需要的水比午夜多得多。因此,他们要求所有可能使用的水,但这会浪费午夜的水分配。问题是,并非所有公司都能正确预见他们的高峰用水量,因此他们会要求更多,希望他们永远不需要担心要求更多。尽管这会使水务管理的容量规划变得困难,但作为交换,公司可以简化和加快其内部流程,因为他们永远不需要再与水务管理部门合作。

Java 虚拟机就是这样做的:它在启动时分配大量内存,然后从那里开始工作。默认情况下,内核只会在 Java 应用实际开始使用内存时分配内存。但是,如果您禁用过量使用,内核将认真对待预留。只有当它确实有足够的资源时,它才会允许分配成功。

但是,这种方法还有一个更严重的问题。假设一家公司开始每天请求一单位的水(而不是以 10 为步长)。最终,您将达到 0 个可用单位的状态。现在这家公司将无法分配更多。这很好,反正谁在乎大公司呢。但问题是小家庭也无法要求更多的水!您将无法建造小型公共卫生间来应对突然涌入的游客。您将无法为附近森林的火灾提供应急用水。

用计算机术语来说:在内存非常低的情况下,如果没有过度使用,您将无法打开新的 xterm,无法通过 ssh 进入您的计算机,也无法打开新选项卡来搜索可能的修复程序。换句话说,禁用过度使用也会使您的桌面在内存不足时变得毫无用处。

3.现在,当一家公司开始使用过多的水时,有一种有趣的处理方法。水管理部门会将其炸毁!字面意思是:它前往餐厅所在地,向其中投掷炸药,然后等待它爆炸。这将立即大大减少该镇的用水需求,以便新人可以入住,您可以创建公共卫生间等。作为市长,您可以重建餐厅,希望这一次它需要更少的水。例如,如果餐厅里已经有太多人,您会告诉人们不要进入餐厅(例如,您将打开更少的浏览器标签)。

这实际上是内核在用尽所有选项并需要内存时所做的:它调用 OOM 终止程序。它会选择一个大型应用程序(基于许多启发式方法)并将其终止,释放大量内存但保持桌面响应。实际上,Android 内核执行此操作甚至更积极:当内存不足时,它会终止最近最少使用的应用程序(相比之下,普通内核只会在万不得已的情况下才这样做)。这在 Android 中称为 Viking Killer。

我认为这是解决问题最简单的方法之一:你没有比这更多的选择,所以为什么不早点克服它,对吧?问题是内核有时会做很多工作来避免调用 OOM 杀手。这就是为什么你看到你的桌面非常慢,而内核却没有采取任何措施。但幸运的是,有一个选项可以自己调用 OOM 杀手!首先,确保魔术 sysrq 键已启用(例如echo 1 | sudo tee /proc/sys/kernel/sysrq),然后每当你觉得内核内存不足时,只需按 Alt+SysRQ、Alt+f。

好的,一切都很好,但你想尝试一下吗?内存不足的情况很容易重现。我有一个非常简单的应用程序。你需要运行两次。第一次运行将确定你有多少可用 RAM,第二次运行将创建内存不足的情况。请注意,此方法假设您已禁用交换(例如执行sudo swapoff -a)。代码和用法如下:

// gcc -std=c99 -Wall -Wextra -Werror -g -o eatmem eatmem.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char** argv)
{
    int limit = 123456789;
    if (argc >= 2) {
        limit = atoi(argv[1]);
    }
    setbuf(stdout, NULL);
    for (int i = 1; i <= limit; i++) {
        memset(malloc(1 << 20), 1, 1 << 20);
        printf("\rAllocated %5d MiB.", i);
    }
    sleep(10000);
    return 0;
}

使用方法如下:

$ gcc -std=c99 -Wall -Wextra -Werror -g -o eatmem eatmem.c
$ ./eatmem
Allocated 31118 MiB.Killed
$ ./eatmem 31110
Allocated 31110 MiB.Killed

第一次调用检测到我们有 31,118 MiB 的可用 RAM。所以我告诉应用程序分配 31,110 MiB RAM,这样内核就不会杀死它,而是会占用我几乎所有的内存。我的系统冻结了:甚至鼠标指针都没有移动。我按下了 Alt+SysRQ、Alt+f,它杀死了我的 eatmem 进程,系统恢复了。

尽管我们介绍了在内存不足情况下应采取的措施,但最好的方法(就像任何其他危险情况一样)是首先避免这种情况。有很多方法可以做到这一点。我见过的一种常见方法是将行为不当的应用程序(如浏览器)放入与系统其他部分不同的容器中。在这种情况下,浏览器将无法影响您的桌面。但预防本身超出了问题的范围,所以我不会写它。

总结:虽然目前没有办法完全避免分页,但您可以通过禁用过度提交来缓解完全系统停止。但在内存不足的情况下,您的系统仍然无法使用,但方式不同。无论上述情况如何,在内存不足的情况下,按 Alt+SysRQ、Alt+f 可终止内核选择的大型进程。您的系统应在几秒钟后恢复响应能力。这假设您已启用神奇的 sysrq 键(默认情况下不启用)。

答案3

将所有临时文件和缓存文件放在一起tmpfs会减少可用 RAM 的数量,因此如果没有这个,可能会导致系统比需要的更快地进行交换。

听起来您有些应用程序依赖某种内核工具或驱动程序,而这些工具或驱动程序已经超载。您没有详细说明除浏览器和索引器之外还有哪些类型的应用程序在使用,以及您已禁用索引器。

您可以尝试切换到消耗资源较少的桌面环境或窗口管理器,例如 LXDE 或 IceWM。在工作中,我使用安装了 LXDE 和 ROX-Filer 的 Linux 系统,以获得非常小的桌面环境。这个 Linux 系统的目的是运行 VMWare Player,这样我就可以同时运行 Windows XP 和 Windows 7。它的硬件规格与您所说的类似,在这种重负载下,我没有遇到太多的响应问题。我没有任何Linux 本身的响应问题(通常是虚拟机有时会让我等上一秒钟,而 2 个虚拟机 + 1 个操作系统共享 1 个磁盘,这是意料之中的事),并且始终能够随时暂停或关闭虚拟机。这包括经常在 Linux 后台运行 Firefox。

所以对我来说,这表明您正在运行的特定应用程序存在一些问题。

您的磁盘驱动器是否启用了 DMA?(使用hdparm)如果您使用全盘加密,则所有磁盘流量都必须经过 CPU,这会抵消 DMA 的大部分优势。其结果是高磁盘流量会导致 CPU 峰值,进而导致整个系统变慢。(编辑:澄清一下,dm-crypt在高磁盘流量期间禁用或使用 DMA 会导致高 CPU 消耗)

答案4

尽管这个问题已经两年多了,而且@ypsu 的回答很棒,但是由于缺乏 RAM 而导致基于 Linux 的系统运行状况变差的情况仍然存在。

以下是我对这个问题的观察:即使我根本没有交换,一旦系统内存不足,我的硬盘指示灯就会亮起,因为磁盘负载为 100%。鉴于这一事实,似乎根本原因是内核试图通过卸载可以从磁盘恢复的内容来释放内存,而这肯定是共享库。由于 GUI 应用程序通常有大量共享库,因此系统似乎认为只需卸载其中一些就足够了,但这只在下一个用户空间操作需要这些卸载的库之前有效。这似乎是导致卸载共享库并重新加载它们的无限循环的最有可能发生的情况。

有一个项目充当用户空间守护进程,在为时已晚之前杀死最耗内存的进程:https://github.com/rfjakob/earlyoom

此外,我曾经对内存需求很大的应用程序(例如 Chrome)使用具有合理内存限制的 Docker 容器。

相关内容