我一直在阅读关于Linux的“swappiness”可调的内容,它控制着内核在不使用的时候将应用程序内存交换到磁盘的程度。 如果你是谷歌这个词,你会得到很多像这样讨论利弊的页面。 简而言之,这个观点是这样的:
如果您的swappiness太低,不活动的应用程序将占用其他程序可能要使用的所有系统内存。
如果您的swappiness过高,当您唤醒那些不活动的应用程序时,将会有一个很大的延迟,因为它们的状态是从磁盘读取的。
这个说法对我来说没有意义。 如果我有一个使用大量内存的非活动应用程序,为什么内核不会将其内存分页到磁盘,并在内存中留下该数据的另一个副本? 这似乎给了两全其美的好处:如果另一个应用程序需要这个内存,它可以立即声明物理内存并开始写入,因为它的另一个副本在磁盘上,当不活动的应用程序被唤醒时可以交换回来向上。 当原始应用程序醒来时,仍然在RAM中的任何页面都可以按原样使用,而不必将其从磁盘中取出。
还是我错过了什么?
根据这一点,这正是Linux所做的。
我仍然试图理解这一点,所以任何权威的联系将不胜感激。
如果我有一个使用大量内存的非活动应用程序,为什么内核不会将其内存分页到磁盘,并在内存中留下该数据的另一个副本?
让我们说,我们做到了。 我们把这个页面写到磁盘上,但是留在内存中。 一段时间后,另一个进程需要内存,所以我们想从第一个进程中启动页面。
我们需要绝对确定地知道第一个进程是否已经将页面写入到磁盘之后修改了页面。 如果有的话,我们必须重新写出来。 我们跟踪这个的方式是,当我们第一次把它写到磁盘上时,把这个进程的写权限带回去。 如果进程尝试再次写入页面,则会出现页面错误。 内核可以注意到,在恢复写入权限并允许应用程序继续之前,进程已经弄脏了页面(因此需要重新写出)。
这就是问题所在。 从页面中取走写入权限实际上是比较昂贵的,特别是在多处理器机器中。 所有CPU清除页面转换缓存以确保它们取消写入权限是非常重要的。
如果该过程确实写入页面,则导致页面错误更为昂贵。 我认为这些不重要的页面最终会带来这样的错误,而这种错误会让我们将其留在记忆中,从而获得我们正在寻找的收益。
那么值得去做吗? 我真的不知道。 我只是想解释为什么把页面留在内存中并不像听起来那么明显。
(*)这整个事物与一个名为Copy-On-Write的机制非常相似,这个机制在fork fork()时使用。 子进程很可能只执行几条指令并调用exec(),所以复制所有父页面将是愚蠢的。 相反,写入权限被拿走,孩子只是允许跑步。 写入时复制是一个胜利,因为页面错误几乎从未被采用:小孩几乎总是立即调用exec()。
即使您将应用程序内存分页到磁盘并将其保存在内存中,您仍然需要决定何时将应用程序视为“不活动”,这就是可交换性的控制。 分页到磁盘在IO方面是昂贵的,你不想经常这样做。 这个等式还有另外一个变量,那就是Linux使用剩余内存作为磁盘缓冲区/缓存。
虚拟机的第一件事是干净的页面,并将其移动到干净的列表中。
当清除匿名内存(没有实际文件后备存储的东西时,可以看到/ proc映射中的段是匿名的,并且没有文件系统vnode存储),VM将要做的第一件事是采取“脏”的页面和“干净”,然后通过写出页面的内容交换。 现在,当虚拟机内存已经不足,并且担心自己可以使用新的空闲页面的时候,它可以通过“干净”页面列表,并根据最近使用的内容以及内存类型他们会将这些页面移动到空闲列表中。
一旦内存页面被放置在空闲列表中,它们就不再与之前的内容相关联。 如果一个程序出现在引用页面之前提供的内存位置,那么程序将会出现严重错误,并且可能会从空闲列表中抓取一个(很可能完全不同的)页面,数据将从磁盘读入页面。 一旦完成,页面实际上仍然是“干净的”,因为它没有被修改。 如果虚拟机选择使用该页面交换RAM中的不同页面,那么该页面将再次被“污染”,或者如果应用程序写入该页面,将会被“弄脏”。 然后过程再次开始。
另外,swappinness对于在商业/交易/在线/等待时间敏感的环境中的服务器应用来说是相当可怕的。 当我有16GB内存盒,我没有运行很多的浏览器和图形用户界面,我通常希望我所有的应用程序几乎固定在内存中。 我的大部分内存往往是8-10GB的Java堆,我从来不想要分页到磁盘,可用的cruft是像mingetty进程(但即使在那些应用程序的glibc页面被其他应用程序共享,实际上使用,所以即使这些无用的进程的RSS大小大部分是共享的,使用的页面)。 我通常看不到实际清理过的16GB中的几个10MB。 我会建议非常低的swappiness数量或服务器零swappiness – 未使用的页面应该是整个RAM的一小部分,并试图回收缓冲区缓存风险交换应用程序页面和延迟点击相对微量的内存运行的应用程序。