前言 进程在运行过程中会在执行,阻塞,就绪状态下来回切换。简单介绍下各个状态的切换原因吧。 从执行->阻塞,一般是进程发生某种事件,例如IO,资源申请等。 从阻塞->就绪,是进程等待的事件已完成,为接下来的执行做好了准备。 从就绪->执行,处于就绪的进程得到了系统的调度,获得了时间片,实现了该进程在CPU上的执行。 从执行->就绪,进程被系统调度,失去了CPU的执行权。一般是时间片使用完毕,或者被其他高优先级的进程抢占。 一、问题现象 如下图,程序直接卡死。。。 进程处于阻塞状态,所有业务线程均被阻塞,失去了CPU执行权。阻塞时长,短的时候在几十秒,长的时候长达2-3分钟,然后又自行恢复,周而复始。 这可是已经上线的实时业务,这不是要了开发的命了。看到这个情况,自己着急得像热锅上的蚂蚁,赶紧在家里开始着手排查。 二、排查思路 看到了问题的现象,可是问题的排查该从哪着手呢? 首先怀疑了自己的业务代码里是否有长时间的业务处理流程。可是就算是有长时间的业务处理,所有线程的cpu不可能都为零呀;其次,难道业务代码里有锁,产生了竞争?不,我们的业务代码里没有互斥使用的对象,也就没有锁。即使有锁,那也至少有一个线程cpu不为0,而且不可能这样长时间为0。 一头雾水啊,到底该从哪里着手分析呢? 从现实回到理论,只有进程被阻塞,未被系统调度的时候,才有可能出现所有线程的CPU使用率为0。 朝着这个大方向开始往下分析,那到底是什么事件,导致了所有线程被阻塞。 有网络IO?不,我们不是一个有大量网络请求的服务端。 有磁盘IO?不,线上业务流量规模不至于导致产生大量磁盘读写,而且通过iotop查看,在阻塞时,系统IO读写带宽还远远未达到磁盘IO的瓶颈。 内存不够啦?但是阻塞的时候查看可用内存也还有1GB以上呀。 机器坏啦?哈哈哈,在实在没有思路的时候,真的出现过这种想法。可是经验告诉我,不到万不得已,怀疑啥都别怀疑系统,甚至怀疑硬件,必定是自己的业务代码问题,或者是某些方法使用不当带来的问题。 那到底是啥导致了所有业务线程的阻塞?这里可以确定的是,线程被阻塞,一定是有事件导致的阻塞。用户态可能性不大,那很有可能是系统调用阻塞在了内核态,那有没有该进程对应的内核态相关信息呢,从哪里看呢? 三、问题排查 如何查看进程运行态相关的内核态信息呢? Linux其实很完备啊,Linux内核提供了pro 文件系统,系统中当前运行的每一个进程在proc下都有一个对应的目录,且以进程的PID号为目录名,用户可以通过进程对应的PID目录得到对应进程的相关运行态信息。 比如ID为93571的进程,有如下示例的一些信息: 例如fd目录记录了当前进程打开的文件id,status文件记录了进程的内存使用和其它众多信息,task目录则包含了该进程及其所有子线程的信息。 那哪个文件记录了进程当前的系统调用链呢?那就是我要找的stack文件,stack会记录当前进程在内核的调用栈,且是实时更新的!打开文件,会有某个进程当前在内核态的调用链信息,比如: 但是问题来了,进程是一个多线程结构,如何查看每一个业务线程当前的内核态调用链呢?答案就是通过task目录获得想要查看的线程内核调用信息。 先看看task里记录的线程id: 再具体查看某个线程id的信息: 发现了吗,上图的记录项与图三里的记录项一模一样,就是说我们也可以具体的了解到某个线程的运行态信息!比如某个业务线程的内核态调用链:
|