宋宝华:swappiness=0究竟意味着什么?

本文解释swappiness的作用,以及swappiness=0究竟意味着什么。

内存回收

我们都知道,Linux一个进程使用的内存分为2种:

  1. file-backed pages(有文件背景的页面,比如代码段、比如read/write方法读写的文件、比如mmap读写的文件;他们有对应的硬盘文件,因此如果要交换,可以直接和硬盘对应的文件进行交换),此部分页面进page cache

  2. anonymous pages(匿名页,如stack,heap,CoW后的数据段等;他们没有对应的硬盘文件,因此如果要交换,只能交换到虚拟内存-swapfile或者Linux的swap硬盘分区),此部分页面,如果系统内存不充分,可以被swap到swapfile或者硬盘的swap分区

因此,Linux在进行内存回收(memory reclaim)的时候,实际上可以从1类和2类这两种页面里面进行回收,而swappiness就决定了回收这2类页面的优先级。

swappiness越大,越倾向于回收匿名页;swappiness越小,越倾向于回收file-backed的页面。当然,它们的回收方法都是一样的LRU算法。


swappiness=0的历史与现在

在Linux的早期版本(2012年以前的版本,kernel 3.5-rc1),哪怕swappiness被设置为0,其实匿名页仍然有被交换出去的机会:

早先的回收权重是这样计算的:

anon_prio = swappiness;

file_prio = 200 - anon_prio;

ap = (anon_prio + 1) * (reclaim_stat->recent_scanned[0] + 1);

fp = (file_prio + 1) * (reclaim_stat->recent_scanned[1] + 1);

由此可看出,哪怕swappiness为0,ap也是不会为0的,只是比较小。所以swappiness=0不意味着匿名页就不交换。

2012年的第一场雪,比以往时候来得更晚一些

这一年,一个小小的提交,引发了蝴蝶效应,并震惊寰宇:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=fe35004fbf9eaf67482b074a2e032abb9c89b1dd


它彻底改变了swappiness=0的定义。这个commit,碧血横飞,浩气四塞,草木为之含悲,风云因而变色。


它的意思再明确不过,如果swappiness=0,除非系统的内存过小nr_free + nr_filebacked < high watermark这种恶劣情况发生

都只是考虑交换file-backed的pages,就不会考虑交换匿名页了。

它改动的代码如下:

-ap = (anon_prio + 1) * (reclaim_stat->recent_scanned[0] + 1);

+ap = anon_prio * (reclaim_stat->recent_scanned[0] + 1);

ap /= reclaim_stat->recent_rotated[0] + 1;

-fp = (file_prio + 1) * (reclaim_stat->recent_scanned[1] + 1);

+fp = file_prio * (reclaim_stat->recent_scanned[1] + 1);

anon_prio如果为0的话,ap也为0了。

于是乎,现在的swappiness如果等于0的话,意味着哪怕匿名页占据的内存很大,哪怕swap分区还有很多的剩余空间,除非恶劣情况发生,都不会交换匿名页,因此这可能造成更大的OOM压力。不像以前,平时会一直兼顾着回收page cache和匿名页。


现在swappiness=0的情况下,天平的格局是:


一石激起千层浪,两指弹出万般音。相关社区的网站内容都跟着进行了更新,比如红帽子:


特洛伊之战中,在决定阿基琉斯和赫克托尔的命运的生死一战中,荷马将命运的天平放在宙斯手中:“天父取出他的那杆黄金天秤,把两个悲惨的死亡判决放进秤盘,一个属阿基琉斯,一个属驯马的赫克托尔,他提起秤杆中央,赫克托尔一侧下倾,滑向哈得斯。”


跨过特洛伊木马屠城千年的悲凉,我们看到Linux里面两位战神的命运,被一个码农轻松地决定。

这个修改引起了一系列的连锁反应,而相关的文档修改,却是发生在2年之后:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8582cb96b0bfd6891766d8c30d759bf21aad3b4d


MEM CGroup里面的swappiness

在使能Memory CGroup的情况下,每个memory group可以设置自己的swappiness值,如果某个group的swappiness被设置为0,这个group的匿名页交换会被完全禁止,从而诱发该group在无file-backed页面可回收情况下(哪怕swap空间还很大)的OOM,这一点透过Documentation/cgroup-v1/memory.txt文档可以看出:

 

5.3 swappiness

Overrides /proc/sys/vm/swappiness for the particular group. The tunable in the root cgroup corresponds to the global swappiness setting.

Please note that unlike during the global reclaim, limit reclaim enforces that 0 swappiness really prevents from any swapping even if there is a swap storage available. This might lead to memcg OOM killer if there are no file pages to reclaim.


Form1->DoubleBuffered = true究竟意味着什么

07-19

从字面上讲当然是“使用双缓冲”,可是:rnrn比如我有如下的一组绘图语句,需要多次执行:rnrn 语句块1,绘制深层背景rnrn 语句块2,绘制浅层背景rnrn 语句块3,绘制前景物体rnrn如果没有缓冲区,那么:当语句块1、2执行完毕后,只能看到背景,当语句块3执行完毕后,才能看到物体,因此在每一祯中,我们都是先看见背景,才看见物体。也就意味着,物体会消失一小段时间,也就是所谓的“闪烁”。rnrn使用缓冲区,就是说,先在内存中虚拟绘图板,语句块1,语句块2,语句块3都在这个虚拟的绘图板上作图。当执行完毕后,将虚拟绘图板的内容绘制到屏幕上。那么,我们看到的将是连贯的动画。rnrn一般情况下,都是由程序员来控制双缓冲(MFC中正是如此),因为只有程序员才知道,在什么情况下,可以将缓冲的内容复制到屏幕上来,在这个例子中就是“当语句块3执行完毕后”。rnrn可是编译器不知道啊!它凭什么知道应该等语句块3结束后再画到屏幕上而不是等语句块2甚至语句块1结束后?可能有人说,要等你全部的绘图都执行完毕。可是,很多情况下,比如某些游戏或者动画中,绘图请求会连绵不断地出现,只有准确找到那个恰当的点,才能有效的实现平滑动画。我想知道BCB是如何判断这个点的。rnrn在我知道Form有DoubleBuffered这个属性之前,我使用自己的办法进行双缓冲,过程如下:rnrn全局量:rn Graphics::TBitmap Buffer;rn Graphics::TBitmap Background = LoadFromFile(...);rnrn在某个Image的DragOver事件中:rnrn Buffer->Canvas->Draw(0, 0, dynamic_cast(Background)); // 绘制背景rn Buffer->Canvas->... // 绘制物体rn Image1->Canvas->Draw(0, 0, dynamic_cast(Buffer)); // 绘制到屏幕rnrn我认为这是一个良好的双缓冲结构,(以前在MFC上一直用类似的方法)。但经过测试发现,只要我不加上Form1->DoubleBuffered = true这句,我自己写的双缓冲一点作用都没有,该闪烁还是照样闪烁。rnrn我把Canvas->Draw理解为MFC中的BitBlt,即复制位图,不知道这样理解是否正确。如果正确的话,那么我的代码不应该引起控件闪烁。很奇怪,请高手指教!

没有更多推荐了,返回首页

私密
私密原因:
请选择设置私密原因
  • 广告
  • 抄袭
  • 版权
  • 政治
  • 色情
  • 无意义
  • 其他
其他原因:
120
出错啦
系统繁忙,请稍后再试