博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
postgres: page allocation failure. order:1, mode:0x20
阅读量:3522 次
发布时间:2019-05-20

本文共 20789 字,大约阅读时间需要 69 分钟。

今天遇到GPseg 的postgre进程被OS kill -6,查看系统的日志发现  page allocation failure.。

当时看到的服务器的内存情况如下图:

报错信息:

Jun 11 10:03:49 P1QMSSDW10 kernel: postgres: page allocation failure. order:1, mode:0x20  可以看出来是GFP_ATOMIC类型的申请,且order = 1(page = 2 )Jun 11 10:03:49 P1QMSSDW10 kernel: Pid: 9234, comm: postgres Tainted: G        W  ---------------    2.6.32-504.el6.x86_64 #1Jun 11 10:03:49 P1QMSSDW10 kernel: Call Trace:Jun 11 10:03:49 P1QMSSDW10 kernel: 
[
] ? __alloc_pages_nodemask+0x74a/0x8d0Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? kmem_getpages+0x62/0x170Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? fallback_alloc+0x1ba/0x270Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? cache_grow+0x2cf/0x320Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? ____cache_alloc_node+0x99/0x160Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? kmem_cache_alloc+0x11b/0x190Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? sk_prot_alloc+0x48/0x1c0Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? sk_clone+0x22/0x2e0Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? inet_csk_clone+0x16/0xd0Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? tcp_create_openreq_child+0x23/0x470Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? tcp_v4_syn_recv_sock+0x4d/0x310Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? tcp_check_req+0x226/0x460Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? tcp_v4_do_rcv+0x35b/0x490Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? mod_timer+0x144/0x220Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? tcp_v4_rcv+0x522/0x900Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? ip_local_deliver_finish+0x0/0x2d0Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? ip_local_deliver_finish+0xdd/0x2d0Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? ip_local_deliver+0x98/0xa0Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? ip_rcv_finish+0x12d/0x440Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? ip_rcv+0x275/0x350Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? __netif_receive_skb+0x4ab/0x750Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? netif_receive_skb+0x58/0x60Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? napi_skb_finish+0x50/0x70Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? napi_gro_receive+0x39/0x50Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? ixgbe_clean_rx_irq+0x26a/0xc90 [ixgbe]Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? ixgbe_poll+0x453/0x7e0 [ixgbe]Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? net_rx_action+0x103/0x2f0Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? __do_softirq+0xc1/0x1e0Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? handle_IRQ_event+0x60/0x170Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? __do_softirq+0x11f/0x1e0Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? call_softirq+0x1c/0x30Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? do_softirq+0x65/0xa0Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? irq_exit+0x85/0x90Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? do_IRQ+0x75/0xf0Jun 11 10:03:49 P1QMSSDW10 kernel: [
] ? ret_from_intr+0x0/0x11Jun 11 10:14:22 P1QMSSDW10 abrt[56116]: Not saving repeating crash in '/opt/greenplum/greenplum-db-4.3.27.0/bin/postgres'Jun 11 10:14:22 P1QMSSDW10 abrt[56118]: Not saving repeating crash in '/opt/greenplum/greenplum-db-4.3.27.0/bin/postgres'Jun 11 10:14:35 P1QMSSDW10 abrt[56119]: Saved core dump of pid 56117 (/opt/greenplum/greenplum-db-4.3.27.0/bin/postgres) to /var/spool/abrt/ccpp-2020-06-11-10:14:22-56117 (1550864384 bytes)

对这段page allocation failer 的解释

1)从第一行log“order:1, mode:0x20”可以看出来是GFP_ATOMIC类型的申请,且order = 1(page = 2 )

2)第一次内存申请尝试

在__alloc_pages_nodemask()里,首先调用 get_page_from_freelist() 尝试第一次申请,使用的标志位是 ALLOC_WMARK_LOW|ALLOC_CPUSET,它会对每个zone都做 zone_watermark_ok()的检查,使用的就是传进的watermark[low]阈值。
在zone_watermark_ok()里会考虑z->lowmem_reserve[],导致在normal上的申请不会落到低端zone。比如对于DMA32:

free pages = 380032KB = 95008 pages < low(1000KB = 250 pages) +  lowmem_reserve[normal](94940) = 95190

所以就认为DMA32也不平不ok,同理更用不了DMA的内存。

而对于normal自己内存来说,free pages = 56552 KB = 14138 pages,也不用考虑lowmem_reserve(0),但这时还会考虑申请order(1),减去order 0的12544个page后只剩 14138 - 12544 = 1594,也小于 low / 2 = (48788KB=12197pages) / 2 = 6098 pages。
所以初次申请尝试失败,进入__alloc_pages_slowpath() 尝试进行更为积极一些的申请。

3)第二次内存申请尝试

__alloc_pages_slowpath()首先是通过 gfp_to_alloc_flags() 修改alloc_pages,设上更为强硬的标志位。这块根据原来的GFP_ATOMIC会设上 ALLOC_WMARK_MIN | ALLOC_HARDER | ALLOC_HIGH。但注意的是不会设上 ALLOC_NO_WATERMARKS 标志位。这个标志位不再判断zone的水位限制,属于优先级最高的申请,可以动用所有的reserve内存,但条件是(!in_interrupt() && ((p->flags & PF_MEMALLOC) || unlikely(test_thread_flag(TIF_MEMDIE)))),即要求不能在中断上下文,且是正在进行回收(例如kswapd)或者正在退出的进程。

之后进入拿着新的alloc_pages重新进入get_page_from_pagelist() 尝试第二次申请,虽然有了 ALLOC_HARDER和ALLOC_HIGH,但是不幸的是在3个zone的zone_watermark_ok检查中还是都无法通过,例如对于DMA32:

free pages = 380032KB = 95008 pages因为设上了ALLOC_HIGH 所以会将得到的watermark[min]减半,即min = min/2 = 800K / 2 = 400K = 100pages而又因为设上了ALLOC_HARDER,会再将min砍去1/4,即min = 3 * min / 4 = 100 pages * 3 / 4 = 75 pages即便如此,min(75 pages) +  lowmem_reserve[normal](94940) = 95015,仍大于free pages,仍认为无法分配内存,同理DMA也不不成功,而normal中 free pages里连续8K的页太少也无法满足分配

第二次失败后,由于没有ALLOC_NO_WATERMARK也不会进入__alloc_pages_high_priority 进行最高优先级的申请,同时由于是GFP_ATOMIC类型的分配不能阻塞回收或者进入OOM,因此就以申请失败告终。

遇到此种情况可以适当调高 min_free_kbytes 使kswapd较早启动回收,使系统一直留有较多的空闲内存,同时可以适度降低 lowmem_reserve_ratio(可选),使得内存不足的情况下(主要是normal zone)可以借用DMA32/DMA的内存救急(注意不能也不能过低)。来自 

查看coreDump reason

ccpp-2020-06-11-10:14:22-56117]# more reason Process /opt/greenplum/greenplum-db-4.3.27.0/bin/postgres was killed by signal 6 (SIGABRT)

又是kill -6,已绝望,上次。 

--------------------------update 2020年7月24日17:06:48

7/21 master 上发生kill -6 ???

 

7/22 

同时10号节点也发生kill -6

 

数据库瞬断。 发生时取查看vmsta,看到的都是free 的内存先是到达低峰,Process 被OS kill后free 被腾出,可是bffer 和cache还有很多,为何不可用呢?

meminfo的解释Buffers %luRelatively temporary storage for raw disk blocks that shouldn't get tremendously larCached %luIn-memory cache for files read from the disk (the page cache). Doesn't include SwapC...SReclaimable %lu (since Linux 2.6.19)Part of Slab, that might be reclaimed, such as caches.SUnreclaim %lu (since Linux 2.6.19)Part of Slab, that cannot be reclaimed on memory pressure.Buffers 是对原始磁盘块的临时存储,也就是用来缓存磁盘的数据,通常不会特别大(20MB 左右)。这样,内核就可以把分散的写集中起来,统一优化磁盘的写入,比如可以把多次小的写合并成单次大的写等等。Cached 是从磁盘读取文件的页缓存,也就是用来缓存从文件读取的数据。这样,下次访问这些文件数据时,就可以直接从内存中快速获取,而不需要再次访问缓慢的磁盘SReclaimable 是 Slab 的一部分。Slab 包括两部分,其中的可回收部分,用SReclaimable 记录;而不可回收部分,用 SUnreclaim 记录。有两个问题:第一个问题,Buffer 的文档没有提到这是磁盘读数据还是写数据的缓存,而在很多网络搜索的结果中都会提到 Buffer 只是对将要写入磁盘数据的缓存。那反过来说,它会不会也缓存从磁盘中读取的数据呢?第二个问题,文档中提到,Cache 是对从文件读取数据的缓存,那么它是不是也会缓存写文件的数据呢?

 

 看到德哥的这篇文章前来拜读。

背景

Linux把物理内存划分为三个层次来管理:存储节点(Node)、管理区(Zone)和页面(Page)。

每一个Node,系统又将其分为多个Zone,x86架构下,node被分为ZONE_DMA、ZONE_NORMAL、ZONE_HIGHMEM,而64位x86架构下有ZONE_DMA、ZONE_DMA32、ZONE_NORMAL。

它们之间的是树状的包含关系:

位于低位的ZONE,内存较少。

对于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.

分配物理内存时,kernel由high zone到low zone依次查找是否有足够的内存可以分配,找到可用的内存后映射到虚拟地址上。

但是低位的ZONE内存较少,所以如果低位的ZONE被占满之后,就算剩余的物理内存很大,还是会出现oom的情况。对于linux2.6来说,oom之后会根据score杀掉一个进程(oom的话题这里不展开了)。

这就引入了对低位ZONE的保护措施,lowmem_reserve_ratio。

如果操作系统的内存很多(比如64GB以上),可以将低位的ZONE的保护加到最大。

例如

sysctl -w vm.lowmem_reserve_ratio='1 1 1'echo "vm.lowmem_reserve_ratio='1 1 1'" >/etc/sysctl.conf

一台192G内存的机器,X86_64 OS,DMA=16MB,DMA32=4GB,NORMAL=剩余的内存。

那么当设置了vm.lowmem_reserve_ratio='1 1 1'后,每一个ZONE的protection数组将变成如下,数组中的对应位置的值,表示当对应的高级ZONE来这个ZONE申请内存时,这个ZONE的剩余内存必须大于这个保护值。

#free -m             total       used       free     shared    buffers     cachedMem:        193031     190099       2931          0        288     168877-/+ buffers/cache:      20933     172098Swap:            0          0          0#cat /proc/zoneinfo |grep prodma:        protection: (0, 498744, 49625144, 49625144)dma32:        protection: (0, 0, 49126400, 49126400)normal:        protection: (0, 0, 0, 0)由于normal已经是最高的ZONE,所以没有更高的ZONE会来它这里申请内存,它的保护值都是0,表示都可以申请.

如DMA, 498744*4K表示DMA32来它这里申请的保护值,当DMA ZONE的剩余内存大于这个保护值时,才允许DMA32来它这里申请内存。

49625144*4K表示NORMAL来它这里申请的保护值,当DMA ZONE的剩余内存大于这个保护值时,才允许NORMAL来它这里申请内存。

这些值是怎么计算的? 根据对应保护位的高位ZONE的内存大小,除以被保护的系数。 如DMA的保护系数为1,即vm.lowmem_reserve_ratio的第一个元素值,那么DMA32来DMA申请内存的保护值=DMA32的内存大小/1,NORMAL来DMA申请内存的保护值=NORMAL的内存大小/1 。

每个ZONE的内存大小(单位4K):

#cat /proc/zoneinfo |grep spandma:        spanned  4095dma32:        spanned  1044480normal:        spanned  49807360

低位内存不足导致的page allocation failure

例如,某台主机,发生了一些这样的报错,导致进程被OOM

Dec 20 17:14:57 server4 kernel:  postmaster: page allocation failure. order:0, mode:0xd0    Dec 20 17:14:57 server4 kernel:  [
] __alloc_pages+0x2e1/0x2f7 Dec 20 17:14:57 server4 kernel: [
] __get_free_pages+0x18/0x24 Dec 20 17:14:57 server4 kernel: [
] kmem_getpages+0x1c/0xbb Dec 20 17:14:57 server4 kernel: [
] cache_grow+0xab/0x138 Dec 20 17:14:57 server4 kernel: [
] cache_alloc_refill+0x165/0x19d Dec 20 17:14:57 server4 kernel: [
] kmem_cache_alloc+0x51/0x57 Dec 20 17:14:57 server4 kernel: [
] mempool_alloc+0xb2/0x135 Dec 20 17:14:57 server4 kernel: [
] autoremove_wake_function+0x0/0x2d Dec 20 17:14:57 server4 kernel: [
] autoremove_wake_function+0x0/0x2d Dec 20 17:14:57 server4 kernel: [
] bio_alloc+0x15/0x168 Dec 20 17:14:57 server4 kernel: [
] sync_page_io+0x25/0xa2 Dec 20 17:14:57 server4 kernel: [
] write_disk_sb+0x5a/0x86 Dec 20 17:15:01 server4 kernel: [
] sync_sbs+0x22/0x2f Dec 20 17:15:01 server4 kernel: [
] md_update_sb+0x84/0xc6 Dec 20 17:15:01 server4 kernel: [
] md_write_start+0x5e/0x8c Dec 20 17:15:01 server4 kernel: [
] make_request+0x22a/0x2b3 [raid1] Dec 20 17:15:01 server4 kernel: [
] generic_make_request+0x18e/0x19e Dec 20 17:15:01 server4 kernel: [
] submit_bio+0xca/0xd2 Dec 20 17:15:01 server4 kernel: [
] test_set_page_writeback+0xad/0xe1 Dec 20 17:15:01 server4 kernel: [
] swap_writepage+0x9a/0xa3 Dec 20 17:15:01 server4 kernel: [
] pageout+0x8d/0xcc Dec 20 17:15:01 server4 kernel: [
] shrink_list+0x207/0x3ed Dec 20 17:15:01 server4 kernel: [
] __pagevec_release+0x15/0x1d Dec 20 17:15:01 server4 kernel: [
] shrink_cache+0x1dd/0x34d Dec 20 17:15:01 server4 kernel: [
] shrink_zone+0xa7/0xb6 Dec 20 17:15:01 server4 kernel: [
] shrink_caches+0x4c/0x57 Dec 20 17:15:01 server4 kernel: [
] try_to_free_pages+0xc3/0x1a7 Dec 20 17:15:01 server4 kernel: [
] __alloc_pages+0x1fe/0x2f7 Dec 20 17:15:01 server4 kernel: [
] __get_free_pages+0x18/0x24 Dec 20 17:15:01 server4 kernel: [
] kmem_getpages+0x1c/0xbb Dec 20 17:15:01 server4 kernel: [
] cache_grow+0xab/0x138

很可能就是低位内存不足导致的。

一些概念(摘自互联网)

lowmem与highmem

关于lowmem和highmem的定义在这里就不详细展开了,推荐两篇文章:

链接内讲的比较清楚,这里只说结论:

1. 当系统的物理内存 > 内核的地址空间范围时,才需要引入highmem概念。

2. x86架构下,linux默认会把进程的虚拟地址空间(4G)按3:1拆分,0~3G user space通过页表映射,3G-4G kernel space线性映射到进程高地址。就是说,x86机器的物理内存超过1G时,需要引入highmem概念。

3. 内核不能直接访问1G以上的物理内存(因为这部分内存没法映射到内核的地址空间),当内核需要访问1G以上的物理内存时,需要通过临时映射的方式,把高地址的物理内存映射到内核可以访问的地址空间里。

4. 当lowmem被占满之后,就算剩余的物理内存很大,还是会出现oom的情况。对于linux2.6来说,oom之后会根据score杀掉一个进程(oom的话题这里不展开了)。

5. x86_64架构下,内核可用的地址空间远大于实际物理内存空间,所以目前没有上面讨论的highmem的问题。

linux的物理内存管理

接下来的问题是,linux是如何实现highmem的概念的?

Linux把物理内存划分为三个层次来管理:存储节点(Node)、管理区(Zone)和页面(Page)。

在NUMA架构下,系统根据CPU的物理颗数,将物理内存分成对应的Node,这里也不展开了,可以参考

每一个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.

linux如何分配内存

这里也不详细展开了,推荐两篇文章:

结论:

1. malloc属于glic的库函数,分配的是虚拟地址。

2. linux的malloc分配时,如果申请内存小于MMAP_THRESHOLD(默认128K),使用brk分配,否则使用mmap分配。

3. 通过brk分配的地址空间,当堆尾的空闲内存超过M_TRIM_THRESHOLD(默认128K)时,执行内存缩紧操作,这里指的也是虚拟地址。

4. 读写内存时,触发缺页中断,此时才会分配物理内存。

5. 分配物理内存时,kernel由high zone到low zone依次查找是否有足够的内存可以分配,找到可用的内存后映射到虚拟地址上。

6. 关于系统分配内存的详细介绍,可以参考:

lowmem_reserve_ratio

这里主要是对vm.txt的解释,建议看原文

为什么要调整lowmem_reserve_ratio

在有高端内存的机器上,从低端内存域给应用层进程分配内存是很危险的,因为这些内存可以通过mlock()系统锁定,或者变成不可用的swap空间。

在有大量高端内存的机器上,缺少可以回收的低端内存是致命的。因此如果可以使用高端内存,Linux页面分配器不会使用低端内存。这意味着,内核会保护一定数量的低端内存,避免被用户空间锁定。“lowmem_reserve_ratio”参数可以调整内核对于lower zone的保护力度。

lowmem_reserve_ratio参数的含义

lowmem_reserve_ratio是一个数组,可以通过以下命令查看:

% cat /proc/sys/vm/lowmem_reserve_ratio    256     256     32

数组的长度=内存zone数量-1,其中每个数并不是绝对值,而是一个比例,代表1/256或1/32。

再次搬出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值:

protection[dma,dma32] = zone_dma32.present/lowmem_reserve_ratio[dma(1)] = 813848/256 = 3179,这个结果对应上面DMA段中,protection数组的第二个元素。

然后需要比较zone_dma.free的值(3933) 与 protectiondma,dma32 + zone_dma.watermarkhigh的大小:

如果free>protection+watermark[high],则可以分配page;否则不能分配,内核继续查找下一个lower zone。

也就是说只有在higher zone内存不足时才会尝试从lower zone继续申请。

更详细的文档可以参考:

根据公式可以看出:

lowmem_reserve_ratio越大,低级的zone中被保护的内存就越小;

lowmem_reserve_ratio越小,低级的zone中被保护的内存就越大;

当lowmem_reserve_ratio=1(100%)时代表对low zone的最大保护强度。

lowmem_reserve_ratio

For some specialised workloads on highmem machines it is dangerous for    the kernel to allow process memory to be allocated from the "lowmem"    zone.  This is because that memory could then be pinned via the mlock()    system call, or by unavailability of swapspace.        And on large highmem machines this lack of reclaimable lowmem memory    can be fatal.        So the Linux page allocator has a mechanism which prevents allocations    which _could_ use highmem from using too much lowmem.  This means that    a certain amount of lowmem is defended from the possibility of being    captured into pinned user memory.        (The same argument applies to the old 16 megabyte ISA DMA region.  This    mechanism will also defend that region from allocations which could use    highmem or lowmem).        The `lowmem_reserve_ratio' tunable determines how aggressive the kernel is    in defending these lower zones.        If you have a machine which uses highmem or ISA DMA and your    applications are using mlock(), or if you are running with no swap then    you probably should change the lowmem_reserve_ratio setting.        The lowmem_reserve_ratio is an array. You can see them by reading this file.    -    % cat /proc/sys/vm/lowmem_reserve_ratio    256     256     32    -    Note: # of this elements is one fewer than number of zones. Because the highest          zone's value is not necessary for following calculation.        But, these values are not used directly. The kernel calculates # of protection    pages for each zones from them. These are shown as array of protection pages    in /proc/zoneinfo like followings. (This is an example of x86-64 box).    Each zone has an array of protection pages like this.        -    Node 0, zone      DMA      pages free     1355            min      3            low      3            high     4            :            :        numa_other   0            protection: (0, 2004, 2004, 2004)            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^      pagesets        cpu: 0 pcp: 0            :    -    These protections are added to score to judge whether this zone should be used    for page allocation or should be reclaimed.        In this example, if normal pages (index=2) are required to this DMA zone and    watermark[WMARK_HIGH] is used for watermark, the kernel judges this zone should    not be used because pages_free(1355) is smaller than watermark + protection[2]    (4 + 2004 = 2008). If this protection value is 0, this zone would be used for    normal page requirement. If requirement is DMA zone(index=0), protection[0]    (=0) is used.        zone[i]'s protection[j] is calculated by following expression.        (i < j):      zone[i]->protection[j]      = (total sums of present_pages from zone[i+1] to zone[j] on the node)        / lowmem_reserve_ratio[i];    (i = j):       (should not be protected. = 0;    (i > j):       (not necessary, but looks 0)        The default values of lowmem_reserve_ratio[i] are        256 (if zone[i] means DMA or DMA32 zone)        32  (others).    As above expression, they are reciprocal number of ratio.    256 means 1/256. # of protection pages becomes about "0.39%" of total present    pages of higher zones on the node.        If you would like to protect more pages, smaller values are effective.    The minimum value is 1 (1/1 -> 100%).

Linux老版本lowmem_reserve参数

摘自 

2.6内核的zone结构中一个成员变量 lowmem_reserve

struct zone {        /* Fields commonly accessed by the page allocator */            /* zone watermarks, access with *_wmark_pages(zone) macros */        unsigned long watermark[NR_WMARK];            /*        * We don't know if the memory that we're going to allocate will be freeable        * or/and it will be released eventually, so to avoid totally wasting several        * GB of ram we must reserve some of the lower zone memory (otherwise we risk        * to run OOM on the lower zones despite there's tons of freeable ram        * on the higher zones). This array is recalculated at runtime if the        * sysctl_lowmem_reserve_ratio sysctl changes.        */        unsigned long       lowmem_reserve[MAX_NR_ZONES];

kernel在分配内存时,可能会涉及到多个zone,分配会尝试从zonelist第一个zone分配,如果失败就会尝试下一个低级的zone(这里的低级仅仅指zone内存的位置,实际上低地址zone是更稀缺的资源)。我们可以想像应用进程通过内存映射申请Highmem 并且加mlock分配,如果此时Highmem zone无法满足分配,则会尝试从Normal进行分配。这就有一个问题,来自Highmem的请求可能会耗尽Normal zone的内存,而且由于mlock又无法回收,最终的结果就是Normal zone无内存提供给kernel的正常分配,而Highmem有大把的可回收内存无法有效利用。

因此针对这个case,使得Normal zone在碰到来自Highmem的分配请求时,可以通过lowmem_reserve声明:可以使用我的内存,但是必须要保留lowmem_reserve[NORMAL]给我自己使用。

同样当从Normal失败后,会尝试从zonelist中的DMA申请分配,通过lowmem_reserve[DMA],限制来自HIGHMEM和Normal的分配请求。

/*    * results with 256, 32 in the lowmem_reserve sysctl:    *  1G machine -> (16M dma, 800M-16M normal, 1G-800M high)    *  1G machine -> (16M dma, 784M normal, 224M high)    *  NORMAL allocation will leave 784M/256 of ram reserved in the ZONE_DMA    *  HIGHMEM allocation will leave 224M/32 of ram reserved in ZONE_NORMAL    *  HIGHMEM allocation will (224M+784M)/256 of ram reserved in ZONE_DMA    *    * TBD: should special case ZONE_DMA32 machines here - in those we normally    * don't need any ZONE_NORMAL reservation    */     #ifdef CONFIG_ZONE_DMA         256,    #endif    #ifdef CONFIG_ZONE_DMA32         256,    #endif    #ifdef CONFIG_HIGHMEM         32,    #endif         32,    };

如果不希望低级zone被较高级分配使用,那么可以设置系数为1,得到最大的保护效果

不过这个值的计算非常的奇怪,来自NORMAL的分配,lowmem_reserve[DMA] = normal_size / ratio,这里使用Normal zone size而不是DMA zone size,这点一直没有想明白。

此外,较新的内核源码目录中/Documentation/sysctl/vm.txt,对lowmem_reserve做了非常准确的描述。

GP 官方建议的OS 参数调整

The sysctl.conf parameters listed in this topic are for performance, optimization, and consistency in awide variety of environments. Change these settings according to your specific situation and setup.Set the parameters in the /etc/sysctl.conf file and reload with sysctl -p:# kernel.shmall = _PHYS_PAGES / 2 # See Shared Memory Pageskernel.shmall = 4000000000# kernel.shmmax = kernel.shmall * PAGE_SIZEkernel.shmmax = 500000000kernel.shmmni = 4096vm.overcommit_memory = 2 # See Segment Host Memoryvm.overcommit_ratio = 95 # See Segment Host Memorynet.ipv4.ip_local_port_range = 10000 65535 # See Port Settingskernel.sem = 500 2048000 200 40960kernel.sysrq = 1kernel.core_uses_pid = 1kernel.msgmnb = 65536kernel.msgmax = 65536kernel.msgmni = 2048net.ipv4.tcp_syncookies = 1net.ipv4.conf.default.accept_source_route = 0net.ipv4.tcp_max_syn_backlog = 4096net.ipv4.conf.all.arp_filter = 1net.core.netdev_max_backlog = 10000net.core.rmem_max = 2097152net.core.wmem_max = 2097152vm.swappiness = 10vm.zone_reclaim_mode = 0vm.dirty_expire_centisecs = 500vm.dirty_writeback_centisecs = 100vm.dirty_background_ratio = 0 # See System Memoryvm.dirty_ratio = 0vm.dirty_background_bytes = 1610612736vm.dirty_bytes = 4294967296

可以看到官方建议将vm.zone_reclaim_mode = 0 设置为0。

参考:

转载地址:http://vthqj.baihongyu.com/

你可能感兴趣的文章
项目整合微信扫码登录功能
查看>>
分布式文件系统FastDfs的搭建
查看>>
Springboot项目利用Java客户端调用FastDFS
查看>>
全文检索工具elasticsearch的安装和简单介绍
查看>>
利用Kibana学习全文检索工具elasticsearch
查看>>
SpringBoot在Test测试类或自定义类中通过@Autowired注入为null
查看>>
使用docker搭建YAPI服务
查看>>
西南科技大学OJ题 邻接表到邻接矩阵1056
查看>>
西南科技大学OJ题 有向图的出度计算1057
查看>>
西南科技大学OJ题 有向图的最大出度计算1059
查看>>
西南科技大学OJ题 带权有向图计算1063
查看>>
oracle主键自增触发器编写
查看>>
String与StringBuilder与StringBuffer三者的差别
查看>>
各种IO流之间的关系和区别
查看>>
SSM如何实现上传单图片
查看>>
SSM环境下java如何实现语音识别(百度语音识别版)
查看>>
ajax方法参数的用法和他的含义
查看>>
数据库基础技巧及用法
查看>>
实用方法:无request参数时获得当前的request的方法
查看>>
JS操作数组常用实用方法
查看>>