Linux服务器背景:
CPUS: 40
MEMORY: 127.6 GB
MACHINE: x86_64 (2199 Mhz)
Linux Kernel: 4.4.121
TASKS: 19411
其实这不算大型服务器,我见过大型的一般内存4T起步,300多个cpu.
故障背景:
客户发现系统卡死,手动触发了kdump.
使用下面命令发现有3092个D状态进程。
crash> ps | grep UN | wc -l
3092
小编头一回碰到这么多进程变成D状态,第一感觉应该就是死锁导致。
于是:
crash> ps | grep UN > UN (导入UN文件慢慢玩)
发现UN里面有大量的ps、sudo这样的进程。
于是挑了一个ps进程查看它的堆栈:
crash> bt 50486
PID: 50486 TASK: ffff881171480c80 CPU: 18 COMMAND: "ps"
#0 [ffff8810d1d9bce0] schedule at ffffffff815f353d
#1 [ffff8810d1d9bd40] rwsem_down_read_failed at ffffffff815f61ea
#2 [ffff8810d1d9bd98] call_rwsem_down_read_failed at ffffffff813271d4
#3 [ffff8810d1d9bde0] down_read at ffffffff815f58b3
#4 [ffff8810d1d9bde8] proc_pid_cmdline_read at ffffffff8126c5e8
#5 [ffff8810d1d9be70] __vfs_read at ffffffff81202fd6
#6 [ffff8810d1d9bee8] vfs_read at ffffffff8120360a
#7 [ffff8810d1d9bf18] sys_read at ffffffff81204382
先看下proc_pid_cmdline_read 函数源码:
static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
size_t _count, loff_t *pos)
{
...
tsk = get_proc_task(file_inode(file));
if (!tsk)
return -ESRCH;
mm = get_task_mm(tsk);
down_read(&mm->mmap_sem);
...
}
void __sched down_read(struct rw_semaphore *sem)
从源码中知道 ps命令是在查询某个进程的状态时,执行down_read()没有获取到要查询进程struct mm_struct上的sem(读写锁)
所以下面开始汇编几个函数,先找到sem的值:
crash> dis proc_pid_cmdline_read
....
0xffffffff8126c5d7 : lea 0x68(%r15),%rax
0xffffffff8126c5db : mov %rax,%rdi
0xffffffff8126c5de : mov %rax,0x18(%rsp)
0xffffffff8126c5e3 : callq 0xffffffff815f58a0
crash> dis down_read
0xffffffff815f58a0 : nopl 0x0(%rax,%rax,1) [FTRACE NOP]
0xffffffff815f58a5 : mov %rdi,%rax
0xffffffff815f58a8 : lock incq (%rax)
0xffffffff815f58ac : jns 0xffffffff815f58b3
0xffffffff815f58ae : callq 0xffffffff813271c0
0xffffffff815f58b3 : retq
crash>