炫技!bug 排查大曝光,涉及Linux 内核的那种
来源 | 码农的荒岛求生(ID: escape-it)
发现问题
分析问题
先来仔细分析一下,既然进程看上去被卡死,那么如果被卡在用户态,那么该进程 CPU 使用率必然很高(死循环之类);如果被卡在内核态,这时进程应该正在进行 IO 或者网络通信等,那么 CPU 使用率应该会很低,现在还能查到进程ID,有了进程ID运行 top 命令看一下:
注意 CPU 那一列,显示 CPU 占用率为0%,我们发现此时该进程几乎没有占用CPU,这基本上是在告诉我们该进程是被卡死在内核态,进程要进入内核态那么就是因为调用了某个阻塞式系统调用导致被操作系统挂起,那么该怎么知道进程调用了什么系统调用呢?
跟踪进程系统调用
strace 命令就用来告诉你这个的,运行 strace 命令来查看一下此时进程调用了什么系统调用:
Oops!strace 命令也被卡死了,无奈,再想想还有其它什么办法。。
跟踪进程用户态运行时栈
有了,可以用 pstack 命令,该命令能打印出进程运行时栈信息,虽然该命令不能追踪到内核,但是可以看到用户态最终调用了什么函数,从而推断出调用了什么系统调用,让我们来运行一下:
和strace一样,pstack 也被卡死了。
古老的ps命令永不过时
$ man ps
这里清楚的写着 WCHAN 指的是当前进程正阻塞在哪个内核函数上。
OK,我们来运行一下 ps 命令:
这里值得注意的是,因为 ps 打印的只是运行ps命令这一时刻相应进程的状态,也就是说运行一次 ps 相当于一次采样,因此你应该多运行几次ps,确保运行结果没有变化,否则只运行一次并且时间足够巧那么有可能会获得到一个错误的线索。
两种进程阻塞状态
从ps打印的结果可以看出,该进程运行状态是D,运行状态D表示什么意思呢?我们再次请教man,发现了这样的信息:
原来进程运行状态D表示 uninterruptible sleep,不可被打断的 sleep,意思是说该进程正在睡觉,就算你拍它一巴掌也不会醒,即该进程当前不响应任何外部信号,此时哪怕 kill 命令都杀不掉该进程(除非内核允许该进程接收 kill 信号),直观感受就是该进程被“卡死”了。
进程阻塞在哪个内核函数上
啊哈,我们终于找到进程此时到底卡死在哪里了!
柳暗花明
You are lucky dog,Say hi to /proc/***/syscall,我们同样可以用简单的 cat 命令去 proc 文件系统中查找,使用/proc后跟进程ID+syscall即可。
WTF。。。这是一串什么鬼东西!
根据内核源码查系统调用
要知道这个数字的含义,我们就需要参考内核代码了,一般在 Linux 系统中必要的内核头文件位于/usr/include目录,在博主 64 位 Linux 机器上,我找到了这个文件:
Gotyou!!!我们可以看到调用了 newfstatat 系统调用,这个系统调用有什么作用呢?让我们再一次问男人(man命令):
$ man newfstatat
得到了这样的信息:
跟踪内核运行时栈
OOOOKey,是时候请出重量级工具了,这就是/proc/PID/stack,通过简单的查看这个文件我们就能知道相应进程在内核中的调用栈!!!就问你 Linux 这种设计有没有很厉害,有没有!!!
真相大白
首先我们来看调用栈的栈顶,栈顶正是 ps 命令 WCHAN 那一列打印出来的,进程在内核中正是因为调用这个函数被卡死的。
接下来我们从调用栈的最底层看,我们发现了系统调用,印证了正是进程调用这个系统调用而导致卡住的。
那么调用这个系统调用发生了什么呢?我们接着往上看,注意这几行:
总结
end
观看视频,参加留言送书活动
↓↓↓
推荐阅读: