内核kswapd进程分析
在第一篇博客记录了Unix Domain Socket的使用以及遇到的问题,文章的最后遗留了一个问题:使用UDP模式下,当操作系统内存处于较低水准时,send发送大块内存经常失败,返回错误:no buffer space available。除此之外,最近在线上服务器的高峰期,偶尔有记录到kswapd0/1这两个内核进程长时间运行,占用了相当一部分CPU和磁盘资源。
很显然,上面这两个问题与操作系统内存管理(分配、回收)息息相关,这篇文章简单记录下问题的分析及内核内存管理的部分知识的学习。
操作系统内存管理体系
目前绝大部分的操作系统采用的内存管理模式都是以分页内存为基础的虚拟内存机制。操作系统将内存以页作为分配和管理的单位,一页的大小通常是4KB大小。用户进程运行在虚拟内存中,使用的是虚拟地址,每个进程都拥有内核为之分配的页表集,记录了虚拟内存页到物理内存页的映射,当CPU执行指令执行使用到内存地址时,会利用硬件MMU(Memory Management Unit)以及进程自己的页表转换为物理地址,当页表中映射不存在时,就会发生缺页中断(Page Fault),内核会为该进程分配真正的物理内存,并更新页表。那么最终分配物理内存的是内核,内核又是如何管理物理内存页的呢? 
物理内存区域划分
内核把所有物理内存划分了两个层级进行管理,分别是node和zone,划分的原因与硬件相关。
- node:随着计算机CPU核数和算力的不断增强,单一的内存总线就会使得访问内存成为系统的瓶颈,因此发展出了
NUMA(Non-uniform Memory Access)架构,将CPU和内存划分为多个节点(node),CPU优先使用和分配当前节点的内存,从而缓解这个问题。
比如我的服务器就是两个node,可以使用numastat命令查看:1 2 3 4 5 6 7 8 9
rongchangyu:~$ numastat -v Node 0 Node 1 Total --------------- --------------- --------------- Numa_Hit 968210261.30 1005455923.13 1973666184.44 Numa_Miss 7764827.36 95644479.26 103409306.62 Numa_Foreign 95644479.26 7764827.36 103409306.62 Interleave_Hit 1642.77 1317.27 2960.04 Local_Node 968169560.45 1005410365.26 1973579925.71 Other_Node 7805528.21 95690037.13 103495565.35
- zone: node之下又划分为多个区域(zone)。分别有
ZONE_DMA、ZONE_DMA32、ZONE_NORMAL、ZONE_HIGHMEM、ZONE_MOVABLE、ZONE_DEVICE,划分的原因也与硬件特性有关。比如在x86上DMA内存区域是物理内存的前16M,这是因为早期的ISA总线上的DMA控制器只有24根地址总线,只能访问16M物理内存。并不是一个节点上要包含所有区域类型,可以通过/proc/zoneinfo查看:1 2 3 4 5 6 7 8 9 10 11 12
rongchangyu:~$ cat /proc/zoneinfo Node 0, zone DMA ... Node 0, zone DMA32 ... Node 0, zone Normal ... Node 0, zone Movable ... Node 1, zone Normal ... Node 1, zone Movable
就像是国家划分了省和市,每个市有自己的市政单位,优先负责本市的事务。内存也是一样,通常区域都有一个内存分配系统,负责内存的分配工作。
物理内存分配系统
分配系统负责页帧的分配,Linux采用的算法叫作伙伴分配系统(Buddy System),负责所有物理页面的分配工作,但只有伙伴系统还不行,因为伙伴系统进行的是大粒度的分配,还需要批发与零售,于是便有了Slab Allocator和Kmalloc。 
