问题现象

  • 系统挂起,无法响应
  • SSH无响应,但可以ping通
  • 报错关键信息包含 hung_task_timeout_secs

原因分析

linux kernel bug相关资料显示

By default Linux uses up to 40% of the available memory for file system caching. After this mark has been reached the file system flushes all outstanding data to disk causing all following IOs going synchronous. For flushing out this data to disk this there is a time limit of 120 seconds by default. In the case here the IO subsystem is not fast enough to flush the data withing 120 seconds. This especially happens on systems with a lot of memory. The problem is solved in later kernels

一般情况下,linux会把可用内存的40%的空间作为文件系统的缓存。当缓存快满时,文件系统将缓存中的数据整体同步到磁盘中。但是系统对同步时间有最大120秒的限制。如果文件系统不能在时间限制之内完成数据同步,则会发生上述的错误。这通常发生在内存很大的系统上。系统内存大,则缓冲区大,同步数据所需要的时间就越长,超时的概率就越大

解决方案

缩小文件系统缓存大小

降低缓存占内存的比例,比如由40%降到10%,则需要同步到磁盘上的数据量会变小,IO写时间缩短,会相对比较平稳。文件系统缓存的大小是由内核参数vm.dirty_ratio 和 vm.dirty_backgroud_ratio控制决定的

vm.dirty_background_ratio 指定当文件系统缓存脏页数量达到系统内存百分之多少时(如5%)就会触发pdflush/flush/kdmflush等后台回写进程运行,将一定缓存的脏页异步地刷入外存 vm.dirty_ratio 则指定了当文件系统缓存脏页数量达到系统内存百分之多少时(如10%),系统不得不开始处理缓存脏页(因为此时脏页数量已经比较多,为了避免数据丢失需要将一定脏页刷入外存),在此过程中很多应用进程可能会因为系统转而处理文件IO而阻塞

通常情况下,vm.dirty_ratio的值要大于vm.dirty_background_ratio的值

sysctl -a | grep dirty

修改(降低)vm.dirty_ratio和vm.dirty_ background_ratio的值

./sbin/sysctl -w vm.dirty_ratio=10
./sbin/sysctl -w vm.dirty_background_ratio=5

参数生效

sysctl -p

修改系统IO调度策略

Linux的IO共有三种调度器:CFQ、Noop、Deadline

CFQ (Completely Fair Scheduler(完全公平调度器))(cfq):它是许多Linux 发行版的默认调度器;它将由进程提交的同步请求放到多个进程队列中,然后为每个队列分配时间片以访问磁盘 Noop调度器(noop):基于先入先出(FIFO)队列概念的Linux内核里最简单的I/O调度器。此调度程序最适合于SSD 截止时间调度器(deadline):尝试保证请求的开始服务时间

Linux发行版的默认采用的是CFQ调度器。此方案就是把CFQ调度器修改为最简单的Noop调度器

查看当前采用的调度器

cat /sys/block/sda/queue/scheduler

修改调度器,调度器的修改立即生效,不必重启内核

echo noop >/sys/block/sda/queue/scheduler

取消120秒时间限制

内核hung task检测机制:进程等待IO时,经常处于D状态(TASK_UNINTERRUPTIBLE),该状态的进程不处理信号,所以kill不掉,如果进程长期处于D状态则不正常

  1. IO路径硬件问题,如硬盘损坏(少数情况会导致长期D,通常会返回错误)
  2. 内核问题

这类问题不好定位,且出现通常不可恢复,kill不掉,一般只能重启恢复 内核针对此种情况开发了一种hung task的检测机制,基本原理是:定时检测系统中处于D状态的进程,如果其处于D状态的时间超过了指定时间(默认120s,可配置),则打印相关堆栈信息,也可以通过proc参数配置使其直接panic

120秒的时间限值由内存参数kernel.hung_task_timeout_secs决定的,如果值设置为0,就是设置为长整型的最大值

查看当前hung_task_timeout_secs值

sysctl -a | grep hung_task_timeout_secs

修改hung_task_timeout_secs值为0

./sbin/sysctl -w kernel.hung_task_timeout_secs=0

参数生效

sysctl  -p

永久修改内核参数

在上述方案中采用sysctl修改内核参数重启后失效,永久修改配置文件/etc/sysctl.conf

vm.dirty_ratio=10
vm.dirty_background_ratio=5

如果系统没有sysctl.conf文件,则可以自己创建sysctl.conf,如果开机启动时系统没有读取sysctl.conf文件进行配置,通过修改启动文件来解决,部分linux启动文件是/etc/init.d/rcS,在末尾添加./sbin/sysctl -p