linux死锁检测的一种思路

前言:

上一篇博文讲述了pstack的使用和原理. 和jstack一样, pstack能获取进程的线程堆栈快照, 方便检验和性能评估. 但jstack功能更加的强大, 它能对潜在的死锁予以提示, 而pstack只提供了线索, 需要gdb进一步的确定.

那Linux下, 如何去检测死锁, 如何让死锁的检测能够更加的智能和方便? 这是本文的核心主旨, 让我们一同分享下思路.

常规做法:

我们来模拟一个出现死锁的程序, 然后通过常规方式来确定是否出现了死锁, 以及在那些线程上出现的.

如下是经典的死锁程序:

注: 线程A获取锁1, 线程B获取锁2, 然后线程A/B分别去获取锁2/1, 两者谁也不松手的, 又不得对方的, 于是duang, duang duang...

使用pstack来快速扫描堆栈:

发现有两个线程均在lock中等待, 存在死锁的嫌疑, 需要gdb后具体确认.

图文解读: 线程10800申请mutex_1(此时被线程10799所有), 而线程10799申请mutex_2(被线程10800所有), 于是线程10800在等待线程10799的释放, 而线程10799在等待线程10800的释放, 于是我们可以确定发生死锁了.

但这种方式, 需要开发人员自己去验证和排除, 复杂的案例就并不轻松了.

在gdb中, 我们可以只能看到mutex对应的线程, 却无法反向获取到线程拥有的mutex列表, 如果有这个信息, 就像jstack工具那样, 获取对死锁的判定, 只要扫下堆栈信息, 就能基本的判定了.

检测模型:

对于死锁, 操作系统课程中, 着重讲述了其常规模型/检测算法/规避建议, 这边不再展开.

一言以蔽之: 死锁的发生, 必然意味着有向图(依赖关系)的构建存在环.

关于检测模型, 我们可以这么假定, 锁为有向边, 申请锁的线程A为起点, 拥有锁的线程B为终点. 这样就形成线程A到线程B的一条有向边. 而众多的锁(边)和线程(点), 就构成了一个有向图.

于是乎, 一个死锁检测的算法, 就转变为图论中有向图的环判断问题. 而该问题, 可以借助成熟的拓扑遍历算法轻易实现.

//拓扑排序:可以测试一个有向图是否有环 void Graph::topsort( ) { Queue q; int counter = 0; q.makeEmpty( ); for each Vertex v if( v.indegree == 0 ) q.enqueue( v ); while( !q.isEmpty( ) ) { Vertex v = q.dequeue( ); counter++; for each Vertex w adjacent to v if( --w.indegree == 0 ) q.enqueue( w ); } if( counter != NUM_VERTICES ) throw CycleFoundException( ); }

解决方案:

检测模型的确定, 让人豁然开朗. 但如何落地实现, 成了另一个拦路虎.

让我们从反向获取线程拥有的锁列表这个思路出发, 如何去实现? 如果我们能像java反射一样, 拦截lock/unlock操作, 并添加汇报线程与锁关系的功能, 那自然能构建有向图. 进而实现自动检测死锁情况.

但是C/C++没有反射, 不过可以在所有的lock/unlock代码处添加桩代码, 并得以解决. 但这对使用方的代码具有侵入性, 能否改善呢?

上天总是眷顾勤奋的人, 这边我们可以借助宏扩展(宏不会递归展开, 这是关键)来巧妙实现这个功能.

#include #define gettid() syscall(__NR_gettid)// 拦截lock, 添加before, after操作, 记录锁与线程的关系#define pthread_mutex_lock(x) do { printf("before=>thread_id:%d apply mutex:%p\n", gettid(), x); pthread_mutex_lock(x); printf("after=>thread_id:%d acquire mutex:%p\n", gettid(), x); } while (false);// 拦截unlock, 添加after操作, 解除锁和线程的关系#define pthread_mutex_unlock(x) do { pthread_mutex_unlock(x); printf("unlock=>thread_id: %d release mutex:%p\n", gettid(), x); } while(false);

注: gettid函数用于获取线程实际的id, 重名名的pthread_mutex_lock/pthread_mutex_unlock宏, 添加了对before/after拦截调用, 并汇报记录了锁与线程的关系.

我们可以对before/after操作, 进行实际的图构建和检测. 而且该宏替换, 轻松解决了代码侵入性的问题.

让我们在回忆jstack的使用, 猜测java就是借助反射, 轻松实现了类似的功能, 使得其能检测死锁情况.

检验效果:

有了上述的理论基础和思路后, 进行尝试和扩展.

这边写了一个简单的检测工具库, 使用非常的简单.

在需要检测的代码中, 引入dead_lock_stub.h头文件, 然后在main函数的开头加入

DeadLockGraphic::getInstance().start_check();

实验效果如下:

样例代码的网盘地址如下: http://pan.baidu.com/s/1ntzHEeX

写在最后:

如果你觉得这篇文章对你有帮助, 请小小打赏下. 其实我想试试, 看看写博客能否给自己带来一点小小的收益. 无论多少, 都是对楼主一种由衷的肯定.

前言:

上一篇博文讲述了pstack的使用和原理. 和jstack一样, pstack能获取进程的线程堆栈快照, 方便检验和性能评估. 但jstack功能更加的强大, 它能对潜在的死锁予以提示, 而pstack只提供了线索, 需要gdb进一步的确定.

那Linux下, 如何去检测死锁, 如何让死锁的检测能够更加的智能和方便? 这是本文的核心主旨, 让我们一同分享下思路.

常规做法:

我们来模拟一个出现死锁的程序, 然后通过常规方式来确定是否出现了死锁, 以及在那些线程上出现的.

如下是经典的死锁程序:

注: 线程A获取锁1, 线程B获取锁2, 然后线程A/B分别去获取锁2/1, 两者谁也不松手的, 又不得对方的, 于是duang, duang duang...

使用pstack来快速扫描堆栈:

发现有两个线程均在lock中等待, 存在死锁的嫌疑, 需要gdb后具体确认.

图文解读: 线程10800申请mutex_1(此时被线程10799所有), 而线程10799申请mutex_2(被线程10800所有), 于是线程10800在等待线程10799的释放, 而线程10799在等待线程10800的释放, 于是我们可以确定发生死锁了.

但这种方式, 需要开发人员自己去验证和排除, 复杂的案例就并不轻松了.

在gdb中, 我们可以只能看到mutex对应的线程, 却无法反向获取到线程拥有的mutex列表, 如果有这个信息, 就像jstack工具那样, 获取对死锁的判定, 只要扫下堆栈信息, 就能基本的判定了.

检测模型:

对于死锁, 操作系统课程中, 着重讲述了其常规模型/检测算法/规避建议, 这边不再展开.

一言以蔽之: 死锁的发生, 必然意味着有向图(依赖关系)的构建存在环.

关于检测模型, 我们可以这么假定, 锁为有向边, 申请锁的线程A为起点, 拥有锁的线程B为终点. 这样就形成线程A到线程B的一条有向边. 而众多的锁(边)和线程(点), 就构成了一个有向图.

于是乎, 一个死锁检测的算法, 就转变为图论中有向图的环判断问题. 而该问题, 可以借助成熟的拓扑遍历算法轻易实现.

//拓扑排序:可以测试一个有向图是否有环 void Graph::topsort( ) { Queue q; int counter = 0; q.makeEmpty( ); for each Vertex v if( v.indegree == 0 ) q.enqueue( v ); while( !q.isEmpty( ) ) { Vertex v = q.dequeue( ); counter++; for each Vertex w adjacent to v if( --w.indegree == 0 ) q.enqueue( w ); } if( counter != NUM_VERTICES ) throw CycleFoundException( ); }

解决方案:

检测模型的确定, 让人豁然开朗. 但如何落地实现, 成了另一个拦路虎.

让我们从反向获取线程拥有的锁列表这个思路出发, 如何去实现? 如果我们能像java反射一样, 拦截lock/unlock操作, 并添加汇报线程与锁关系的功能, 那自然能构建有向图. 进而实现自动检测死锁情况.

但是C/C++没有反射, 不过可以在所有的lock/unlock代码处添加桩代码, 并得以解决. 但这对使用方的代码具有侵入性, 能否改善呢?

上天总是眷顾勤奋的人, 这边我们可以借助宏扩展(宏不会递归展开, 这是关键)来巧妙实现这个功能.

#include #define gettid() syscall(__NR_gettid)// 拦截lock, 添加before, after操作, 记录锁与线程的关系#define pthread_mutex_lock(x) do { printf("before=>thread_id:%d apply mutex:%p\n", gettid(), x); pthread_mutex_lock(x); printf("after=>thread_id:%d acquire mutex:%p\n", gettid(), x); } while (false);// 拦截unlock, 添加after操作, 解除锁和线程的关系#define pthread_mutex_unlock(x) do { pthread_mutex_unlock(x); printf("unlock=>thread_id: %d release mutex:%p\n", gettid(), x); } while(false);

注: gettid函数用于获取线程实际的id, 重名名的pthread_mutex_lock/pthread_mutex_unlock宏, 添加了对before/after拦截调用, 并汇报记录了锁与线程的关系.

我们可以对before/after操作, 进行实际的图构建和检测. 而且该宏替换, 轻松解决了代码侵入性的问题.

让我们在回忆jstack的使用, 猜测java就是借助反射, 轻松实现了类似的功能, 使得其能检测死锁情况.

检验效果:

有了上述的理论基础和思路后, 进行尝试和扩展.

这边写了一个简单的检测工具库, 使用非常的简单.

在需要检测的代码中, 引入dead_lock_stub.h头文件, 然后在main函数的开头加入

DeadLockGraphic::getInstance().start_check();

实验效果如下:

样例代码的网盘地址如下: http://pan.baidu.com/s/1ntzHEeX

写在最后:

如果你觉得这篇文章对你有帮助, 请小小打赏下. 其实我想试试, 看看写博客能否给自己带来一点小小的收益. 无论多少, 都是对楼主一种由衷的肯定.


相关内容

  • 分析linux中的死锁处理策略
  • 分析linux中的死锁处理策略 汪建国 摘要:进程死锁问题是操作系统的主要问题之一,很多学者专家一直在研究怎样解决这个问题.本文针对操作系统中经常出现的死锁问题进行了讨论,阐述了死锁出现的原因.四个必要条件,以及死锁的处理方法. 关键词:死锁:死锁产生的原因:死锁产生的条件:死锁的解除与预防:银行家 ...

  • 网络工程师企业面试问题
  • 企业网管必备技术入门 公司面试模拟试题 如果你去一个企业面试网管,可能会碰到这样的技术考题.先自我测量一下吧,答案回复可见. 1. 路由器的基本功能? 2. win2000有那两种远程访问方法? 拨号远程访问和VPN(虚拟专用网络) 3. 出两道英文题,比如:What about your comp ...

  • 多处理机系统的死锁检测方法[综述]
  • 多处理机系统的死锁检测方法 摘要:在多处理机系统中,并发性得到了有效利用,但并发程序的不确定性使得死锁检测十分困难.针对现有的工作集中在使用分析.验证或测试的单一途径来检测死锁这一问题,本文通过分析现有工具的死锁检测能力,提出了综合使用工具的死锁检测方法.同时根据分析.验证和测试途径的不同特点,给出 ...

  • 同步和互斥的区别
  • 信号量 互斥量 条件变量 同步和互斥 一.信号量: 用于进程间通信(linux 中用于线程) 独立于进程 在两个进程中都要初始化,若一个已创建,则另一个获取 可以执行多次v 操作,然后再执行p 操作 信号量的一些概念: 以下是信号灯(量) 的一些概念: 信号灯与互斥锁和条件变量的主要不同在于&quo ...

  • 死锁的检测与解除C语言代码
  • 实验名称:死锁的检测与解除 姓名:杨秀龙 学号: 1107300432 指导老师:霍林 实验题目 死锁的检测与解除 实验目的 为了更清楚系统对死锁是如何检测和当死锁发生时如何解除死锁 设计思想 首先需要建立和银行家算法类似的数组结构,先把孤立的进程(没有占用资源的进程)放入一个数组中,根据死锁原理, ...

  • 阻塞与死锁(三)--死锁的定位及解决方法
  • 死锁所在的资源和检测: 在SQL Server的两个或多个任务中,如果某个任务锁定了其他任务试图锁定的资源.会造成这些任务的永久阻塞,从而出现死锁. 下图为例: l  事务T1获得了行R1的共享锁. l  事务T2获得了行R2的共享锁. l  然后事务T1请求行R2的排它锁,但是T2完成并释放其对R ...

  • 预防交叉口死锁的信号配时方法
  • [摘 要]由于交通瓶颈处的排队车辆上溯到上游交叉口,导致的上游交叉口车流不能正常运行.造成这种现象的主要原因是信号配时不合理造成的前车预估不足,后车盲目跟随导致交叉口拥堵.目前,信号配时方法通常假定车辆能够正常通过交叉口实现信号配时,但交通过饱和状态下车辆往往难以正常通过交叉口,形成"死锁 ...

  • 事业单位考试计算机专业试题
  • 事业单位招考计算机专业考试试卷 计算机专业考试试卷 一. 单项选择题:(共35分,1-35题每题1分) 1. 以帧为传送数据单位的是:( D ) A. 会话层B. 数据链路层C. 网络层D. 传输层 2. ATM传输数据的单位是信元,每个信元( D )是个字节. A. 5 B. 48 C. 53 D ...

  • 百度2016校招笔试题(含答案.解析)
  • 解析来源:七月题库APP 1.vsftpd配置本地用户传输速率的参数( ) A:anon_max_rate B:user_max_rate C: max_user D: local_max_rate 答案:D 解析:vsftpd 是一个在类UNIX 操作系统上运行的FTP服务器,它是一个完全免费的. ...