最近小伙伴们在排查一个线上关于linux内存oom的问题,前些天来问我某篇文章里的一句话是什么含义,问题比较难用几句话说明,在这里梳理一下。
最近小伙伴们在排查一个线上关于内存oom的问题,前些天来问我某篇文章里的一句话是什么含义:
每次申请的block大小比较有讲究,Linux内核分为LowMemroy和HighMemroy,LowMemory为内存紧张资源,LowMemroy有个阀值,通过free -lm和/proc/sys/vm/lowmem_reserve_ratio来查看当前low大小和阀值low大小。低于阀值时候才会触发oom killer,所以这里block的分配小雨默认的256M,否则如果每次申请512M(大于128M),malloc可能会被底层的brk这个syscall阻塞住,内核触发page cache回写或slab回收。
出自:
http://blog.csdn.net/gugemichael/article/details/24017515
感觉这句话说的混合了好几个概念,并且说的跟自己的理解有冲突,在此说一下自己的理解。对内核研究不深,一些概念可能理解有误,因此文章里给出了比较可靠的参考文档,有兴趣可以进一步了解详情。
关于lowmem和highmem的定义在这里就不详细展开了,推荐两篇文章:
链接内讲的比较清楚,这里只说结论:
接下来的问题是,linux是如何实现highmem的概念的?
Linux把物理内存划分为三个层次来管理:存储节点(Node)、管理区(Zone)和页面(Page)。
在NUMA架构下,系统根据CPU的物理颗数,将物理内存分成对应的Node,这里也不展开了,可以参考 NUMA (Non-Uniform Memory Access): An Overview 。
每一个Node,系统又将其分为多个Zone,x86架构下,node被分为ZONE_DMA、ZONE_NORMAL、ZONE_HIGHMEM,而64位x86架构下有ZONE_DMA(ZONE_DMA32)和ZONE_NORMAL。它们之间的是树状的包含关系:
可以通过以下命令查看numa node信息:
$ numactl --hardware available: 2 nodes (0-1) node 0 cpus: 0 2 4 6 8 10 12 14 16 18 20 22 node 0 size: 8114 MB node 0 free: 2724 MB node 1 cpus: 1 3 5 7 9 11 13 15 17 19 21 23 node 1 size: 8192 MB node 1 free: 818 MB node distances: node 0 1 0: 10 20 1: 20 10
可以通过以下命令查看zone信息,注意单位是page(4k):
$ cat /proc/zoneinfo Node 0, zone DMA pages free 3933 min 20 low 25 high 30 scanned 0 spanned 4095 present 3834 ……………………
结合之前关于highmem的说明,对于x86架构的系统来说,物理内存被划分为:
类型 | 地址范围 |
---|---|
ZONE_DMA | 前16MB内存 |
ZONE_NORMAL | 16MB – 896MB |
ZONE_HIGHMEM | 896 MB以上 |
ZONE_DMA位于低端的内存空间,用于某些旧的ISA设备。ZONE_NORMAL的内存直接映射到Linux内核线性地址空间的高端部分。
对于x86_64架构的系统来说:
类型 | 地址范围 |
---|---|
ZONE_DMA | 前16MB内存 |
ZONE_DMA32 | 16MB – 4G |
ZONE_NORMAL | 4G以上 |
和x86相比,多了ZONE_DMA32,少了ZONE_HIGHMEM.
这里也不详细展开了,推荐两篇文章:
结论:
关于系统分配内存的详细介绍,可以参考: Memory Mapping and DMA 。
在有高端内存的机器上,从低端内存域给应用层进程分配内存是很危险的,因为这些内存可以通过mlock()系统锁定,或者变成不可用的swap空间。
在有大量高端内存的机器上,缺少可以回收的低端内存是致命的。因此如果可以使用高端内存,Linux页面分配器不会使用低端内存。这意味着,内核会保护一定数量的低端内存,避免被用户空间锁定。“lowmem_reserve_ratio”参数可以调整内核对于lower zone的保护力度。
lowmem_reserve_ratio是一个数组,可以通过以下命令查看:
% cat /proc/sys/vm/lowmem_reserve_ratio 256 256 32
数组的长度=内存zone数量-1,其中每个数并不是绝对值,而是一个比例。
再次搬出zoneinfo,这里以zone_dma和zone_dma32举例:
$ cat /proc/zoneinfo Node 0, zone DMA pages free 3933 min 20 low 25 high 30 scanned 0 spanned 4095 present 3834 protection: (0, 3179, 7976, 7976) Node 0, zone DMA32 pages free 639908 min 4456 low 5570 high 6684 scanned 0 spanned 1044480 present 813848 protection: (0, 0, 4797, 4797) ……………………
linux尝试在zone中分配page时,会判断当前zone的page_free与高位zone的page_present的关系。
例如在dma中尝试申请dma32的page时,有如下计算:
protection[dma,dma32] = zone_dma32.present/lowmem_reserve_ratio[dma(1)] = 813848/256 = 3179,对应DMA段中,protection数组的第二个元素。
can_allocate = zone_dma.free(3933) > ( protectiondma,dma32 + zone_dma.high(30) ) = true,表示可以分配(实际上只有在high zone内存不足时才会申请低位内存)
更详细的文档可以参考: Documentation/sysctl/vm.txt
根据公式可以看出: