问题现象
客户发现在物理机上跑读写业务时,出现了一次死机现象,kdump服务未抓到vmcore文件。/var/log/messages里没有发现内核panic报错信息,只有call trace的警告信息。抓取到的call trace信息总共有三种类型:内存分配失败、rmmod、任务超时。
问题不稳定复现,客户分析是在新读写业务开始调试长加电运行后,大概两周多左右出现了死机问题。业务设备由于诸多原因无法邮寄,也无法提供远程环境。但问题紧急程度很高,要求快速解决。
因没有生成vmcore不好准确分析,于是第一时间提供客户监测系统多种性能的脚本放到后台运行,运行一周左右取出这段时间监测到的数据信息以及系统messages。此时系统虽未死机,但已成功复现了上述三类call trace,下文将对抓取到的日志进行分析
日志分析
内存分配失败日志分析
系统message分析
page allocation failure的call trace输出,以下内核输出的时间戳在15:39:46左右:
kernel: [67442.657502] mount: page allocation failure: order:0, mode:0x440000(GFP_NOWAIT|__GFP_THISNODE), nodemask=(null)
kernel: [67442.658760] mount cpuset=/ mems_allowed=0,2,4,6,8,10,12,14
kernel: [67442.659451] CPU: 31 PID: 872267 Comm: mount Kdump: loaded Tainted: G W OE K 4.19.90-25.2.aarch64 #1
kernel: [67442.660681] Hardware name: ... ...
kernel: [67442.661595] Call trace:
kernel: [67442.661910] dump_backtrace+0x0/0x170
kernel: [67442.662392] show_stack+0x24/0x30
kernel: [67442.662809] dump_stack+0xa4/0xe8
kernel: [67442.663256] warn_alloc+0xc4/0x128
kernel: [67442.663664] __alloc_pages_nodemask+0xcd4/0xd70
... ...
kernel: [67442.673551] __arm64_sys_mount+0x28/0x38
kernel: [67442.674033] el0_svc_common+0x78/0x130
kernel: [67442.674505] el0_svc_handler+0x38/0x78
kernel: [67442.674966] el0_svc+0x8/0x1b0
本次失败是申请2^0个page时失败,即连续1个页的内存(pagesize为64kB),一般order 0分配失败代表着系统已经没有内存了。
因GFP_NOWAIT代表不可睡眠等待,__GFP_THISNODE代表从指定内存节点中分配并且无回退机制,没有指定zone则默认是ZONE_NORMAL。怀疑可能是当时指定的node节点里zone normal的order 0链表内没有找到空闲且匹配成功的页,故而没有重试直接返回了申请失败。根据日志中call trace前,业务软件的输出表示将任务绑定到了node 0上,猜测可能是node 0上没有空闲页。
由日志信息可见,死机时pid为872267的进程调用__alloc_pages_nodemask直接申请物理内存。由底层堆栈知,这段过程应该是由用户态的应用程序调用mount系统调用,触发el0中断,从而陷入内核。
内核中调用warn_alloc的函数有:__alloc_pages_slowpath、__vmalloc_area_node、__vmalloc_node_range、vmemmap_alloc_block。根据堆栈函数调用流程知,进入的内存分配函数是__alloc_pages_nodemask,在此函数内会通过get_page_from_freelist快速内存分配。快速分配失败以后,会去调用__alloc_pages_slowpath进行慢速内存分配。在__alloc_pages_slowpath里,先会尝试唤醒kswapd进程回收内存,因本次未设置__GFP_KSWAPD_RECLAIM属性,故未走这个流程。之后,重新尝试分配了一次内存,但是仍旧失败了。失败后的流程需要判断当前是否可以进行内存回收操作,若回收后依旧分配失败,则会进入OOM检查流程。因本次未设置__GFP_DIRECT_RECLAIM属性,故直接判断为内存申请失败就调warn_alloc报错返回,没有走内核direct reclaim回收内存的流程,进而也没有触发OOM。
为了详细了解当时的内存状态,进一步分析meminfo输出。
第一段meminfo输出:
kernel: [67442.726710] Node 0 DMA32 free:119232kB min:1664kB low:2560kB high:3456kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:595072kB writepending:0kB present:2031616kB managed:907904kB mlocked:595072kB kernel_stack:0kB pagetables:128kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:1472kB
kernel: [67442.729920] lowmem_reserve[]: 0 1913 1913
kernel: [67442.730433] Node 0 Normal free:124032kB min:58752kB low:90048kB high:121344kB active_anon:1985152kB inactive_anon:13248kB active_file:803840kB inactive_file:7035136kB unevictable:18799680kB writepending:320kB present:31391744kB managed:31353600kB mlocked:18799680kB kernel_stack:17328kB pagetables:14528kB bounce:0kB free_pcp:34112kB local_pcp:128kB free_cma:0kB
kernel: [67442.733879] lowmem_reserve[]: 0 0 0
kernel: [67442.734339] Node 2 Normal free:12869376kB min:62784kB low:96256kB high:129728kB active_anon:1365696kB inactive_anon:29376kB active_file:2222784kB inactive_file:2928960kB unevictable:12462720kB writepending:2240kB present:33554432kB managed:33516416kB mlocked:12462720kB kernel_stack:18512kB pagetables:19008kB bounce:0kB free_pcp:66944kB local_pcp:64kB free_cma:0kB
kernel: [67442.737912] lowmem_reserve[]: 0 0 0
kernel: [67442.738457] Node 4 Normal free:1002240kB min:62784kB low:96256kB high:129728kB active_anon:1204288kB inactive_anon:9600kB active_file:283264kB inactive_file:4446656kB unevictable:25140416kB writepending:2240kB present:33554432kB managed:33516480kB mlocked:25140416kB kernel_stack:5776kB pagetables:15616kB bounce:0kB free_pcp:65856kB local_pcp:1024kB free_cma:0kB
kernel: [67442.741901] lowmem_reserve[]: 0 0 0
kernel: [67442.742368] Node 6 Normal free:15073536kB min:62784kB low:96256kB high:129728kB active_anon:475520kB inactive_anon:20096kB active_file:153216kB inactive_file:1919424kB unevictable:15502400kB writepending:1024kB present:33554432kB managed:33516480kB mlocked:15502464kB kernel_stack:10272kB pagetables:8448kB bounce:0kB free_pcp:64896kB local_pcp:1024kB free_cma:0kB
kernel: [67442.745760] lowmem_reserve[]: 0 0 0
kernel: [67442.746199] Node 8 Normal free:3885312kB min:62656kB low:96064kB high:129472kB active_anon:1126464kB inactive_anon:1024kB active_file:528768kB inactive_file:2995776kB unevictable:23091456kB writepending:768kB present:33488896kB managed:33450816kB mlocked:23091456kB kernel_stack:11856kB pagetables:11840kB bounce:0kB free_pcp:51456kB local_pcp:832kB free_cma:0kB
kernel: [67442.749695] lowmem_reserve[]: 0 0 0
kernel: [67442.750213] Node 10 Normal free:13484864kB min:62784kB low:96256kB high:129728kB active_anon:2751808kB inactive_anon:448kB active_file:714112kB inactive_file:3798400kB unevictable:12332672kB writepending:1088kB present:33554432kB managed:33516416kB mlocked:12332672kB kernel_stack:7648kB pagetables:4864kB bounce:0kB free_pcp:49216kB local_pcp:832kB free_cma:0kB
kernel: [67442.753654] lowmem_reserve[]: 0 0 0
kernel: [67442.754118] Node 12 Normal free:19964416kB min:62720kB low:96192kB high:129664kB active_anon:333568kB inactive_anon:2048kB active_file:7296kB inactive_file:558144kB unevictable:12332480kB writepending:64kB present:33554432kB managed:33492928kB mlocked:12332480kB kernel_stack:2592kB pagetables:768kB bounce:0kB free_pcp:33728kB local_pcp:896kB free_cma:0kB
kernel: [67442.757542] lowmem_reserve[]: 0 0 0
kernel: [67442.758019] Node 14 Normal free:20600384kB min:62720kB low:96192kB high:129664kB active_anon:356416kB inactive_anon:640kB active_file:12416kB inactive_file:7232kB unevictable:12332480kB writepending:0kB present:33554432kB managed:33474368kB mlocked:12332480kB kernel_stack:2256kB pagetables:1472kB bounce:0kB free_pcp:27136kB local_pcp:896kB free_cma:0kB
kernel: [67442.761357] lowmem_reserve[]: 0 0 0
除了Node 0 DMA32,其他节点均未留有CMA预留区域。所有Node Normal区域的free值均高于low、min水位线,说明当前系统仍有空闲内存。但一般情况下min以下的内存是不会被分配的,故各节点Normal区域可分配内存大小为,Node 0:65280 kB,Node 2:12806592 kB,Node 4:939456 kB,Node 6:15010752 kB,Node 8:3822656 kB,Node 10:13422080 kB,Node 12:19901696 kB,Node 14:20537664 kB,各节点内存充足。分配失败有可能是order 0链表上没有空闲页导致的。
第二段meminfo输出:
kernel: [67442.761808] Node 0 DMA32: 27*64kB (UMC) 4*128kB (UMC) 3*256kB (MC) 1*512kB (M) 1*1024kB (M) 0*2048kB 0*4096kB 0*8192kB 1*16384kB (M) 1*32768kB (M) 1*65536kB (M) 0*131072kB 0*262144kB 0*524288kB = 119232kB
kernel: [67442.763860] Node 0 Normal: 867*64kB (UME) 221*128kB (ME) 61*256kB (UME) 18*512kB (ME) 8*1024kB (UME) 2*2048kB (M) 0*4096kB 0*8192kB 0*16384kB 0*32768kB 0*65536kB 0*131072kB 0*262144kB 0*524288kB = 120896kB
kernel: [67442.765863] Node 2 Normal: 732*64kB (UME) 1020*128kB (UME) 532*256kB (UME) 99*512kB (UME) 101*1024kB (ME) 91*2048kB (UME) 59*4096kB (UM) 43*8192kB (UM) 21*16384kB (UM) 10*32768kB (UME) 5*65536kB (UME) 3*131072kB (UME) 1*262144kB (U) 19*524288kB (UM) = 12864256kB
kernel: [67442.768538] Node 4 Normal: 328*64kB (UME) 562*128kB (UME) 482*256kB (UM) 174*512kB (UM) 60*1024kB (UM) 56*2048kB (UM) 35*4096kB (UM) 24*8192kB (UM) 8*16384kB (UM) 2*32768kB (UM) 0*65536kB 0*131072kB 0*262144kB 0*524288kB = 1018112kB
kernel: [67442.770803] Node 6 Normal: 1168*64kB (UME) 903*128kB (UME) 323*256kB (UME) 186*512kB (UME) 93*1024kB (UM) 72*2048kB (UME) 40*4096kB (UM) 29*8192kB (UME) 18*16384kB (ME) 14*32768kB (ME) 9*65536kB (UME) 3*131072kB (UM) 1*262144kB (E) 23*524288kB (M) = 15069824kB
kernel: [67442.773294] Node 8 Normal: 566*64kB (UME) 276*128kB (UME) 140*256kB (UME) 142*512kB (UM) 137*1024kB (UME) 40*2048kB (UM) 34*4096kB (UME) 24*8192kB (UME) 14*16384kB (UM) 12*32768kB (UME) 9*65536kB (UME) 1*131072kB (M) 5*262144kB (UME) 1*524288kB (M) = 3916672kB
kernel: [67442.775762] Node 10 Normal: 37*64kB (UME) 77*128kB (ME) 14*256kB (ME) 45*512kB (UME) 114*1024kB (M) 58*2048kB (UME) 61*4096kB (UME) 46*8192kB (UME) 26*16384kB (UM) 10*32768kB (ME) 4*65536kB (UME) 2*131072kB (UM) 1*262144kB (E) 21*524288kB (M) = 13451200kB
kernel: [67442.778295] Node 12 Normal: 297*64kB (UME) 190*128kB (UME) 97*256kB (UME) 52*512kB (UM) 57*1024kB (UM) 53*2048kB (UME) 35*4096kB (M) 16*8192kB (ME) 14*16384kB (UM) 8*32768kB (UM) 9*65536kB (UME) 4*131072kB (UME) 2*262144kB (ME) 33*524288kB (M) = 19967552kB
kernel: [67442.780749] Node 14 Normal: 165*64kB (UME) 134*128kB (UME) 53*256kB (UME) 37*512kB (UME) 48*1024kB (UM) 33*2048kB (UME) 29*4096kB (UM) 15*8192kB (UME) 8*16384kB (M) 6*32768kB (ME) 5*65536kB (UME) 3*131072kB (ME) 3*262144kB (UME) 35*524288kB (M) = 20603712kB
上图输出了每个Node的区域内order链表情况,可知所有normal区域的order 0链表均有空余内存,迁移类型为unmovable、movable、reclaimable,但是仍然分配失败。怀疑是内存碎片导致的order 0链表里没有连续的64kB的页。
Call trace发生前后半小时内,系统各类监测值分析
- free
call trace发生前,可分配的物理内存数量持续减少,buff/cache占总内存的峰值大约为11%。Call trace发生后,可分配的物理内存数量增多,buff/cache占用极少的比例。整个过程里,free占比未低于30%。说明物理内存充足。
- Vmstat
未使用swap空间,虚拟内存容量充足。Call trace发生前,根据cache、buff、bi、bo值的变化,可知有大量读写任务,系统将数据放入缓冲区的速度大于将数据取出交给设备IO的速度,导致buff持续增多,可能导致系统内存不足。
根据procs r知在运行队列中等待的进程数持续增长,但同时cpu id显示cpu长期空闲,结合设备读写数值,猜测大多数等待的进程是在等待IO资源,cpu算力充足。Call trace发生半小时后,buff/cache内存占有率大幅度下降,应该是内存不足触发系统将这部分内存回收了,而缓冲区大小的减少导致此时bi、bo、r数值飘高。
- Top
call trace发生后半小时,进程oStor对cpu的占有率仍然非常高,load average数值大幅度增长,cpu idle比例也降了10%。平台cpu为两颗S2500/64,load average占cpu核数的17%左右,说明cpu资源充足。
- Iostat
cpu一直较为空闲,整个过程里进行了大量的磁盘IO操作,没有丢块。
总结
根据上述分析可知,call trace因可分配空闲内存不足而触发:
- call trace发生前后半小时左右,系统持续执行大量的读写任务,由于磁盘读写速度慢于缓冲区缓存的速度,导致缓冲区占用的内存持续增长,系统可分配的空闲内存比例减少。
- 系统显示有大量空闲内存,但不可分配给进程,猜测是内存碎片导致空闲内存均不连续,进而无法分配。
- 内存回收机制可缓解内存碎片、释放缓冲区,从而增加可分配空闲内存。当水位线低于low时,系统会唤醒kswapd进程回收内存,若水位线低于min时,系统会通过内核触发direct reclaim直接回收内存。通过上述数据,知call trace发生后,系统成功地将buff/cache的大部分内存回收了,系统回收机制工作正常。
优化思路
避免此类call trace发生有两个方向:增大内存容量,缓解内存碎片。
解决内存碎片可以尝试及时回收内存,可以通过将/proc/sys/vm/zone_reclaim_mode设置为1,当前环境下zone_reclaim_mode值为0。zone_reclaim_mode对应的是内核代码里node_reclaim_mode的值。在分配函数get_page_from_freelist内,会遍历zone链表,若当前zone内存不够,会判断node_reclaim_mode值是否为0,若为0则继续遍历其他zone,若为1则直接对当前zone进行内存回收,提高了内存回收的概率。
rmmod引发的call trace
系统message分析
rmmod的call trace输出:
kernel: [63375.659792] ------------[ cut here ]------------
kernel: [63375.660345] WARNING: CPU: 44 PID: 41590 at kernel/jump_label.c:638 jump_label_del_module+0xdc/0x1d0
kernel: [63375.661339] Modules linked in: disksense(OEK-) ... ...
kernel: [63375.669409] CPU: 44 PID: 41590 Comm: rmmod Kdump: loaded Tainted: G OE K 4.19.90-25.2.aarch64 #1
kernel: [63375.670712] Hardware name: ... ...
kernel: [63375.671697] pstate: 40000005 (nZcv daif -PAN -UAO)
kernel: [63375.672272] pc : jump_label_del_module+0xdc/0x1d0
kernel: [63375.672846] lr : jump_label_del_module+0xdc/0x1d0
kernel: [63375.673410] sp : ffff81785dc83cd0
kernel: [63375.673830] x29: ffff81785dc83cd0 x28: ffff81785d097400
kernel: [63375.674467] x27: 0000000000000000 x26: 0000000000000000
kernel: [63375.675094] x25: 0000000056000000 x24: ffff24e3a5c19a10
kernel: [63375.675828] x23: ffff24e3a54c6930 x22: ffff24e378e700f0
kernel: [63375.676452] x21: ffff24e378e70000 x20: ffff24e3a5da08f0
kernel: [63375.677040] x19: ffff24e378e72540 x18: 0000000000000001
kernel: [63375.677635] x17: 0000000000000001 x16: 0000000000000007
kernel: [63375.678257] x15: ffffffffffffffff x14: ffff24e425e2bf2f
kernel: [63375.678874] x13: ffff24e3a5e2bf42 x12: ffff24e3a5bfddc0
kernel: [63375.679494] x11: 0000000005f5e0ff x10: ffff24e3a5bd5968
kernel: [63375.680116] x9 : ffff24e3a57c0018 x8 : ffff24e3a4b65998
kernel: [63375.680756] x7 : 7563205b2d2d2d2d x6 : 00000000000012b4
kernel: [63375.681390] x5 : 0000000000000001 x4 : ffff83dc3ef813a0
kernel: [63375.682039] x3 : ffff83dc3ef813a0 x2 : 0000000000000007
kernel: [63375.682672] x1 : 0000000000000006 x0 : 0000000000000024
kernel: [63375.683277] Call trace:
kernel: [63375.683583] jump_label_del_module+0xdc/0x1d0
kernel: [63375.684106] jump_label_module_notify+0x78/0x358
kernel: [63375.684643] notifier_call_chain+0x5c/0xa0
kernel: [63375.685084] blocking_notifier_call_chain+0x68/0xc0
kernel: [63375.685722] __arm64_sys_delete_module+0x224/0x2c0
kernel: [63375.686249] el0_svc_common+0x78/0x130
kernel: [63375.686676] el0_svc_handler+0x38/0x78
kernel: [63375.687090] el0_svc+0x8/0x1b0
kernel: [63375.687448] ---[ end trace d2eb9998b4fe61d8 ]---
这个call trace是由rmmod指令引发的,本次没有像page allocation failure的call trace一样打出meminfo,可用的信息不多。查看log里这次call trace发生前的上文内容,会发现内核模块disksense在工作。从上文disksense的输出看,所有操作均成功完成了,没有失败报错。因"disksense: remove disk[sda]"操作之后才发生call trace,猜测可能是模块disksense执行了rmmod操作,从而触发了本次call trace。
代码分析
代码分析:
根据输出中的“kernel/jump_label.c:638 jump_label_del_module”,去查看相应代码。内核中调用jump_label_del_module的只有以下两处:
static int
jump_label_module_notify(struct notifier_block *self, unsigned long val,void *data)
{... ...switch (val) {case MODULE_STATE_COMING:ret = jump_label_add_module(mod);if (ret) {WARN(1, "Failed to allocate memory: jump_label may not work properly.\n");jump_label_del_module(mod);//第一处}break;case MODULE_STATE_GOING:jump_label_del_module(mod);//第二处break;... ...}
... ...
}
因call trace之前没有“Failed to allocate memory”输出,知本次call trace走的是MODULE_STATE_GOING分支。根据内核中的定义,得知MODULE_STATE_GOING代表模块当前正在被卸载,符合前面对call trace上文的分析。
查看内核代码kernel/jump_label.c里638行的jump_label_del_module,发现是由于内存不足导致的warn_on调用。
jump_label_del_module代码:
637 /* No memory during module load */
638 if (WARN_ON(!jlm))
639 continue;
总结
经上分析,本类call trace同样是可分配内存不足导致的。当系统内存回收速度低于申请内存的速度,就有可能触发此类call trace。
优化思路
因与page allocation failure的call trace触发原因相同,解决方法也同上,调整/proc/sys/vm/zone_reclaim_mode的值为1。
任务超时引发的call trace
系统message分析
超时call trace:
kernel: [64395.445978] INFO: task sync:63328 blocked for more than 120 seconds.
kernel: [64395.446778] Tainted: G W OE K 4.19.90-25.2.aarch64 #1
kernel: [64395.447711] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
kernel: [64395.448565] sync D 0 63328 63278 0x00000200
kernel: [64395.449189] Call trace:
kernel: [64395.449519] __switch_to+0xe8/0x150
kernel: [64395.449937] __schedule+0x298/0x958
kernel: [64395.450352] schedule+0x2c/0x88
kernel: [64395.450735] wb_wait_for_completion+0x5c/0x90
kernel: [64395.451235] sync_inodes_sb+0xb0/0x2b8
kernel: [64395.451687] sync_inodes_one_sb+0x28/0x38
kernel: [64395.452183] iterate_supers+0xb0/0x178
kernel: [64395.452651] ksys_sync+0x3c/0xa0
kernel: [64395.453084] __arm64_sys_sync+0x14/0x20
kernel: [64395.453591] el0_svc_common+0x78/0x130
kernel: [64395.454056] el0_svc_handler+0x38/0x78
kernel: [64395.454529] el0_svc+0x8/0x1b0
systemd[1]: session-244.scope: Succeeded.
kernel: [64402.830405] kauditd_printk_skb: 18 callbacks suppressed
“INFO:task … blocked for more than 120 seconds”是内核hung task机制的警告,内核进程khungtaskd会监控状态为TASK_UNINTERRUPTIBLE的进程是否在120秒内转变状态,如果超时则会输出此警告信息。查看堆栈发现sync_inodes_sb是对块的脏节点同步回写的操作函数,说明call trace发生时系统正处于脏页回写的状态。因等待磁盘IO,sync进程的状态会切换为TASK_UNINTERRUPTIBLE,猜测可能是IO子系统将缓存数据写入磁盘的速度不够快,未能在120秒内完成,从而触发了本次告警信息。
代码分析
hung_task_timeout_secs对应到内核的代码中是sysctl_hung_task_timeout_secs,值默认是120。在内核代码watchdog函数里(watchdog是khungtaskd进程的主函数),每隔sysctl_hung_task_timeout_secs秒就调用check_hung_uninterruptible_tasks检查所有进程。check_hung_uninterruptible_tasks会遍历所有进程,若当前进程状态为TASK_UNINTERRUPTIBLE,则调函数check_hung_task判断是否该进程已经sysctl_hung_task_timeout_secs秒未被唤醒过,若已超时则输出“INFO:task … blocked for more than 120 seconds”的告警信息。
现象分析
当系统脏页同步回写操作耗时较长,导致因IO操作被阻塞的进程超时的情况下,会触发本类call trace。耗时长可能与磁盘IO速度有关,还可能和当时系统内待同步的数据量有关,缓冲区越大,意味着同步所需的时间越长。由于系统上持续运行着应用程序跑读写业务,怀疑当时系统缓存脏页较多,加上磁盘IO积压的任务较多,导致回写操作超过了120秒。
当缓存脏页所占内存的比例低于阈值dirty_background_ratio时,linux系统会唤醒flush等进程在后台执行异步的缓存回写操作,将一定数量的脏页写入磁盘里,此时应用程序仍可以执行磁盘读写操作。当缓存脏页所占内存的比例低于阈值dirty_ratio时,linux系统会触发flush进程进行同步的回写操作,此时应用程序的磁盘读写操作会被阻塞。
怀疑本次call trace是因应用程序发起大量的IO申请,导致脏页所占内存比例降到第一个阈值dirty_background_ratio,而后台异步脏页回写速度没有应用程序占用缓存的速度快,导致脏页所占内存比例持续降到了第二个阈值dirty_ratio。如果此种情况得不到缓解,大量进程将被阻塞,可能导致系统卡顿;如果是核心内核进程被阻塞,可能会导致系统卡死。
优化思路
缓解此类call trace,可以通过调整dirty_background_ratio和dirty_ratio的值来实现。默认情况下,dirty_background_ratio = 10,dirty_ratio = 40,这代表着linux系统最多分配40%的内存空间用于缓存,当缓存占10%内存时会开启脏页回写流程回收一部分脏页。因系统内存较大,大约256GB,linux分配的缓冲区大小也很大,导致将缓冲区数据向磁盘回写所需的时间较长,从而触发超时call trace的概率更高。此情况下,根据调优经验,较合适的数值为:dirty_background_ratio = 5,dirty_ratio = 10。降低两个比例的阈值,能使系统及时回写脏页,在磁盘IO速度没有优势的情况下,降低每次待回写的缓存量,从而减少回写操作的耗时,达到减少阻塞进程超时的目的。
解决方案及验证效果
经过上述分析,可知前两类call trace是系统可分配空闲内存不足导致的两种现象,而第三类call trace是系统分配的缓冲区大小过大导致脏页回写超时现象。综合评估,提出以下两个解决措施。
措施1:解决内存碎片,设置 /proc/sys/vm/zone_reclaim_mode为1。
措施2:调整系统脏页回写,设置 /proc/sys/vm/dirty_ratio为10,设置/proc/sys/vm/dirty_background_ratio为5。
设置方法:
在/etc/sysctl.conf文件末尾加上下图三行参数,然后执行sysctl -p更新到内核。
设置如上三个参数后,客户现场在测试机上持续跑业务程序,连测了一个月一直未出现死机,证明措施有效。
zone_reclaim_mode的官方解释
下方出自linux内核Documentation,是关于zone_reclaim_mode的官方解释:
zone_reclaim_mode可以控制内存回收算法的激进程度,在设置为1后仍不能有效解决可分配内存不够的情况下,还可以改为2、4测试,但是可能会影响系统性能。
PS:内存问题排查技巧
问题发生在两年前,排查记录最近才有空整理出来。当时太菜了。。。。只想到看日志输出
内存不足问题往往没法正常生成vmcore,而日志如果有alloc failed报错的话大概率是内存不足。
如果有调试环境,实际上内存问题最先应排查的是/proc/下的buddyinfo和pagetypeinfo的状态,能很清晰的看出具体是哪个node内存可能有问题 …
如果call trace或者panic的堆栈不完整,比如出现非内核地址例如0x2000…之类的(不是0xfff…开头的)、或者只打印出来两三层(最底层还不是系统调用),那么可以怀疑内存被篡改,开始往非法访问内存方向排查(不排除内存跳bit可能性)。
内存相关问题排查可查询的内核数据:
- /proc/meminfo:全局内存汇总的统计值
root@sujing-GW-001N1B-FTF:/proc# cat meminfo
MemTotal: 8077916 kB
MemFree: 443352 kB
MemAvailable: 2263828 kB
PageSize: 16 kB
Buffers: 60800 kB
Cached: 2198512 kB
SwapCached: 88 kB
Active: 5409368 kB
... ...
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
Hugetlb: 0 kB
root@sujing-GW-001N1B-FTF:/proc#
- /proc/zoneinfo:单独打印每个zone的内存信息
root@sujing-GW-001N1B-FTF:/proc# cat zoneinfo
Node 0, zone DMA32per-node statsnr_inactive_anon 244316nr_active_anon 1079548nr_inactive_file 167680nr_active_file 270832... ...nr_written 467399nr_kernel_misc_reclaimable 0pages free 28688min 2648low 3310high 3972spanned 524288... ...pagesetscpu: 0count: 13high: 378batch: 63vm stats threshold: 30cpu: 1count: 2high: 378batch: 63... ...vm stats threshold: 30node_unreclaimable: 0start_pfn: 524288
Node 0, zone Normalpages free 84432min 8615low 10768high 12921spanned 34078720present 1572864managed 1536799protection: (0, 0, 0)nr_free_pages 84432nr_zone_inactive_anon 174227... ...
- /proc/buddyinfo:能把每个order链表上的页数都打印出来
root@sujing-GW-001N1B-FTF:/proc# cat buddyinfo
Node 0, zone DMA32 2483 3619 1374 711 280 48 101 7 9 8 0
Node 0, zone Normal 5959 4727 1319 2405 1361 621 177 58 3 0 0
- /proc/pagetypeinfo:按不同页块类型,打印相应的数量
root@sujing-GW-001N1B-FTF:/proc# cat pagetypeinfo
Page block order: 9
Pages per block: 512Free pages count per migrate type at order 0 1 2 3 4 5 6 7 8 9 10
Node 0, zone DMA32, type Unmovable 19 23 25 27 29 18 14 5 4 7 0
Node 0, zone DMA32, type Movable 2675 3702 1544 717 261 29 87 2 4 3 0
Node 0, zone DMA32, type Reclaimable 0 0 0 0 0 0 0 0 0 0 0
Node 0, zone DMA32, type HighAtomic 0 0 0 0 0 0 0 0 0 0 0
Node 0, zone DMA32, type CMA 1 1 0 1 0 1 0 0 1 0 0
Node 0, zone DMA32, type Isolate 0 0 0 0 0 0 0 0 0 0 0
Node 0, zone Normal, type Unmovable 413 463 315 150 93 57 26 21 3 2 0
Node 0, zone Normal, type Movable 92 593 572 99 116 16 6 1 0 0 0
Node 0, zone Normal, type Reclaimable 512 566 574 2164 1174 546 148 36 0 0 0
Node 0, zone Normal, type HighAtomic 0 0 0 0 0 0 0 0 0 0 0
Node 0, zone Normal, type CMA 0 0 0 0 0 0 0 0 0 0 0
Node 0, zone Normal, type Isolate 0 0 0 0 0 0 0 0 0 0 0 Number of blocks type Unmovable Movable Reclaimable HighAtomic CMA Isolate
Node 0, zone DMA32 18 952 0 0 16 0
Node 0, zone Normal 187 2646 239 0 0 0
root@sujing-GW-001N1B-FTF:/proc#