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条内存水位线。
内存水位线
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_high
和 pages_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
负责页面回收,主要满足如下两个目标:
- zone内的空闲内存超过高水位,这主要是通过回收order=0的页面;
- 要求zone内的内存在0到给定order之间平衡分布,这主要需要通过内存整理,将连续若干小order的的页面合并为大order页面。
从Linux 4.6开始,kswapd
将更聚焦于内存回收,从kswapd
分离出的kcompactd
将专注于内存整理,旨在解决如下2个问题:
- 目前内存整理都是由于系统缺少大order内存而被触发执行的,不利于维护整个系统内存在0到给定order之间平衡分布;
- 大order的内存分配延时很高;