CPU和IO空闲但负载较高问题排查这里写自定义目录标题
- 背景
- 问题分析
- 系统平均负载的计算
- 为什么会存在大量`D`状态的进程
- 处理`D`状态的进程
背景
一台线上服务器,监控告警显示服务器负载较高,达到了100多。登录机器使用top
命令查看,CPU和IO使用率均较低。同时,发现系统中存在较多ps
和pidof
的进程,且他们的进程状态均为D
,使用kill
命令无法杀死这些进程。
问题分析
初步怀疑是这些进程状态为D
的大量ps
和pidof
的进程导致的负载较高。
通过man ps
查询进程的状态描述信息:
D uninterruptible sleep (usually IO)
R running or runnable (on run queue)
S interruptible sleep (waiting for an event to complete)
T stopped, either by a job control signal or because it is being traced
W paging (not valid since the 2.6.xx kernel)
X dead (should never be seen)
Z defunct ("zombie") process, terminated but not reaped by its parent
根据描述,进程状态D
表示uninterruptible sleep (usually IO)
,即不可中断的休眠,通常是IO操作导致。
查阅资料得知,处于D
状态的进程通常是在等待 IO,比如磁盘 IO,网络 IO,其他外设 IO。处在这种状态的进程不接受外来的任何信号,故而无法通过kill
来杀死它。
那么,为什么处于D
状态的进程会导致机器平均负载升高呢?
系统平均负载的计算
以单CPU是为例,比如过去的平均一分钟里面,判断系统处于运行或者等待状态的进程数则表示系统的平均负载。但是在linux
系统下稍有不同,那些处于IO等待状态的进程也会被纳入计算。所以CPU利用率和平均负载很不同,在大部分进程都在做IO处理的时候,可能CPU利用率很低,但是平均负载很大。
在我们的场景中,存在大量处于D
状态的进程,这些进程本质上都处于IO等待状态,会被纳入到平均负载的计算当中。故而,到最后了机器平均负载升高。
为什么会存在大量D
状态的进程
处于D
状态的进程通常是在等待 IO,出现大量进程处于D
状态通常有两种原因,一是IO设备故障,二是内核IO操作死锁。在我们的场景中就是由于集团监控端和我们自己的监控端都在频繁调用ps
和pidof
,其内部异常导致内核IO操作死锁。
处理D
状态的进程
首先需要判断是否是IO设备故障,如果是IO设备故障,只能尝试修复IO设备。
如果是内核IO操作死锁,可以尝试杀死所有异常进程看是否能够释放锁。
由于处理D
状态的进程没法直接kill
,可以尝试修改进程状态,然后kill
掉进程,具体操作如下:
1、编写内核模块
源码文件: killd.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
MODULE_LICENSE("BSD");
static int pid = -1;
module_param(pid, int, S_IRUGO);
static int killd_init(void)
{
struct task_struct * p;
// force D status process to death
for_each_process(p){
if(p->pid == pid){
set_task_state(p, TASK_STOPPED);
return 0;
}
}
return 0;
}
static void killd_exit(void)
{
// do nothing
}
module_init(killd_init);
module_exit(killd_exit);
2、编译内核模块
2.1 Makefile文件:
bj-m := killd.o
2.2 获取kernel名称:
uname -r
2.3 编译:
make -C /lib/modules/<kernel名称>/build M=`pwd` modules
3 加入模块的时候传入进程号,将进程状态转换为stopped状态
3.1 创建pids
文件,进程号按行加入到文件中
3.2 创建stop.sh
文件:
cat pids | while read line
do
echo $line
insmod ./killd.ko pid=$line && rmmod killd
kill -9 $line
done
3.3 执行
chmod 755 stop.sh && ./stop.sh
通过杀死uninterruptible sleep
状态进程的方式,极有可能无法解决IO操作的死锁问题,甚至可能会引起其他的一些系统异常,最简单的有效的方式是reboot
重启机器。