一次OOM问题排查

字数 1102阅读 63

问题描述

用户问题:

用户发现自己的服务器CPU在某一时刻陡然升高,但从监控上看,同一时刻的业务量却并不高,客户怀疑是云服务器有问题,希望技术支持团队予以解决。

经过我们的排查,发现cpu的两次间歇飙高是由于客户系统当时发生了OOM(out of memory)的情况,并触发了oom-killer造成的。但客户并不接受这个结论,认为是云服务器的异常导致了cpu飙高,而cpu的升高又导致了oom情况的发生。也就是对于cpu升高和oom谁为因果这件事上,客户和我们持完全相反的态度。

下面我们将通过对oom时系统日志的解读来说明cpu升高和oom之间的因果关系。

知识点梳理

1 预备知识

在解读日志之前,我们先回顾一下linux内核的内存管理。

1.1 几个基本的概念

(1)Page 页

处理器的最小‘寻址单元’是字节或者字,而页是内存的‘管理单元’。

(2) Zone 区

(a)区存在的原因:

有些硬件设备只能对特定的内存地址执行DMA(direct memory access)操作。

在一些架构中,实际物理内存是比系统可寻址的虚拟内存要大的,这就导致有些物理内存没有办法被永久的映射在内核的地址空间中。

区的划分也是直接以上面两个原因为依据的。

(b)区的种类 

ZONE_DMA—这个区包含的page可以执行DMA操作。这部分区域的大小和CPU架构有关,在x86架构中,该部分区域大小限制为16MB。

ZONE_DMA32—类似于ZOME_DMA, 这个区也包含可以执行DMA操作的page。该区域只存在于64位系统中,适合32位的设备访问。

ZONE_NORMAL—这个区包含可以正常映射到地址空间中的page,或者说这个区包含了除了DMA和HIGHMEM以外的内存。许多内核操作都仅在这个区域进行。

ZONE_HIGHMEM—这个区包含的是high memory,也就是那些不能被永久映射到内核地址空间的页。

32位的x86架构中存在三种内存区域,ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM。根据地址空间划分的不同,三个区域的大小不一样:

1)1G内核空间/3G用户空间

ZONE_DMA< 16M

ZONE_NORMAL16M~896M

ZONE_HIGHMEM> 896M

2) 4G内核空间/4G用户空间

ZONE_DMA< 16M

ZONE_NORMAL16M~3968M

ZONE_HIGHMEM> 3968M

64位的系统由于寻址能力的提高,不存在highmem区,所以64位系统中存在的区有DMA,DMA32和NORMAL三个区。

ZONE_DMA< 16M

ZONE_DMA3216M~4G

ZONE_NORMAL> 4G

1.2 内核分配内存的函数

下面是内核分配内存的核心函数之一,它会分配2的order次方个连续的物理页内存,并将第一页的逻辑地址返回。

unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)

内核空间的内存分配函数和用户空间最大的不同就是每个函数会有一个gfp_mask参数。

其中gfp 代表的就是我们上面的内存分配函数 __get_free_pages()。

gfp_mask可以分成三种: 行为修饰符(action modifier),区修饰符 (zone modifier)和类型( type).

(1)行为修饰符是用来指定内核该如何分配内存的。比如分配内存时是否可以进行磁盘io,是否可以进行文件系统操作,内核是否可以睡眠(sleep)等等。

(2)区修饰符指定内存需要从哪个区来分配。

(3)类型是行为修饰符和区修饰符结合之后的产物。在一些特定的内存分配场合下,我们可能需要同时指定多个行为修饰符和区修饰符,而type就是针对这些固定的场合,将所需要的行为修饰符和区修饰符都整合到了一起,这样使用者只要指定一个type就可以了。

阅读原文

推荐阅读更多精彩内容