Linux常见内核进程说明 - kswapd和kcompactd

Posted by 云谷计算 on January 26, 2019

kswapd

kswapd内核进程在每个numa节点上运行一个。下面是一个2P服务器上kswap的分布情况,kswapd0和kswapd1分别运行在cpu38和cpu27上,他们在两个不同的numa节点上。

# ps -eF|grep kswap
root         315       2  0     0     0  38 Apr22 ?        00:00:55 [kswapd0]
root         316       2  0     0     0  27 Apr22 ?        00:00:57 [kswapd1]

Linux为每个numa节点通过一个zone来维护空闲内存。空闲内存按照2^N页面大小,分别挂入不同的链表。/proc/buddyinfo反映了每个node节点空闲内存的分布。比如下面在numa节点0上,目前有3466个4k内存页,3691个8k内存页;

# cat /proc/buddyinfo
Node 0, zone      DMA      1      1      1      0      2      1      1      0      1      1      3
Node 0, zone    DMA32    862    670    352    346    198     88     38     22     27     12     62
Node 0, zone   Normal   3466   3691   1647    708    228    256      0      0      0      0      0
Node 1, zone   Normal    753 264718 173632 122550  31937    345     93     72      4      4      1

kswapd线程每100毫秒起来工作一次,或者由于别的进程分配内存失败,而被唤醒。如果是kswapd定期唤醒,它的任务就是让每一个zone的空闲内存都超过高水位。 Linux内核维护了3条内存水位线。

image

内存水位线

pages_min

#cat /proc/sys/vm/min_free_kbytes
3145728

或者

#cat /proc/zoneinfo  | grep min
min      63
min      7139
min      779229

两者对比验证基本保持一致:

#echo "3145728/4" |bc
786432
#echo "63+7139+779229" |bc
786431

pages_low

#cat /proc/zoneinfo  | grep low
        low      4
        low      468
        low      35001
        low      0
        low      0
        low      0
        low      0
        low      36132
        low      0
        low      0

pages_high

#cat /proc/zoneinfo  | grep high | grep -v :
        high     7
        high     789
        high     58991
        high     0
        high     0
        high     0
        high     0
        high     60897
        high     0
        high     0

在上面的例子中,numa节点0的low和high分别为35001和58991,节点1的分别为36132和60897。kswapd用来检查pages_highpages_low,如果可用内存少于 pages_low,kswapd 就开始扫描并试图释放 32个页面,并且重复扫描释放的过程直到可用内存大于pages_high 为止。扫描的时候检查3件事:

  • 如果页面没有修改,把页放到可用内存列表里;
  • 如果页面被文件系统修改,把页面内容写到磁盘上;
  • 如果页面被修改了,但不是被文件系统修改的,把页面写到交换空间。

如果kswapd是被唤醒的,说明有另一个进程在分配内存的时候遇到了麻烦。在唤醒kswapd的同时,这个进程还会把它正在试图分配的order等参数提交给kswapd, 除了保证zone内的空闲内存超过高水位,还得保证空闲内存在0~order之间平衡分布。所以如果达不到平衡分布,就还得继续回收以及尝试小order向大order的组装。

kcompactd

在linux中使用buddy算法解决物理内存的外碎片问题,其把所有空闲的内存,以2的幂次方的形式,分成11个块链表,分别对应为1、2、4、8、16、32、64、128、256、512、1024个页块。

而Linux支持NUMA技术,对于NUMA设备,NUMA系统的结点通常是由一组CPU和本地内存组成,每一个节点都有相应的本地内存,因此buddyinfo 中的Node0表示节点ID;而每一个节点下的内存设备,又可以划分为多个内存区域(zone),因此下面的显示中,对于Node0的内存,又划分类DMA、Normal、HighMem区域。而后面则是表示空闲的区域。

此处以Normal区域进行分析,第二列值为5664,表示当前系统中normal区域,可用的连续两页的内存大小为56642PAGE_SIZE;第三列值为1708,表示当前系统中normal区域,可用的连续四页的内存大小为17082^2PAGE_SIZE

# cat /proc/buddyinfo
Node 0, zone      DMA      1      1      0      0      2      1      1      0      1      1      3
Node 0, zone    DMA32      2      5     16     16     14      3      2      2      3      0    355
Node 0, zone   Normal   9518   5664   1708    531    159     77     44     16      6      6  11536
Node 1, zone   Normal   1346    921    437    116    122     79     38     20     14      5  13223

传统的kswapd负责页面回收,主要满足如下两个目标:

  1. zone内的空闲内存超过高水位,这主要是通过回收order=0的页面;
  2. 要求zone内的内存在0到给定order之间平衡分布,这主要需要通过内存整理,将连续若干小order的的页面合并为大order页面。

从Linux 4.6开始,kswapd将更聚焦于内存回收,从kswapd分离出的kcompactd将专注于内存整理,旨在解决如下2个问题:

  1. 目前内存整理都是由于系统缺少大order内存而被触发执行的,不利于维护整个系统内存在0到给定order之间平衡分布;
  2. 大order的内存分配延时很高;

参考