最近写服务,经常是单进程,多线程的,加了各种锁,很担心出现死锁问题,专门学习了一下死锁问题的诊断。
死锁 (deallocks): 是指两个或两个以上的进程(线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或 系统产生了死锁,这些永远在互相等待的进程(线程)称为死锁进程(线程)。 由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程(线程)在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象 死锁。
一种交叉持锁死锁的情形,此时执行程序中两个或多个线程发生永久堵塞(等待),每个线程都在等待被其它线程占用并堵塞了的资源。例如,如果线程 1 锁住了记录 A 并等待记录 B,而线程 2 锁住了记录 B 并等待记录 A,这样两个线程就发生了死锁现象。在计算机系统中 , 如果系统的资源分配策略不当,更常见的可能是程序员写的程序有错误等,则会导致进程因竞争资源不当而产生死锁的现象。
产生死锁的四个必要条件
(1) 互斥条件:一个资源每次只能被一个进程(线程)使用。
(2) 请求与保持条件:一个进程(线程)因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件 : 此进程(线程)已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件 : 多个进程(线程)之间形成一种头尾相接的循环等待资源关系。
pstack 在 Linux 平台上的简单介绍
pstack 是 Linux(比如 Red Hat Linux 系统、Ubuntu Linux 系统等)下一个很有用的工具,它的功能是打印输出此进程的堆栈信息。可以输出所有线程的调用关系栈。
gdb 在 Linux 平台上的简单介绍
GDB 是 GNU 开源组织发布的一个强大的 UNIX 下的程序调试工具。Linux 系统中包含了 GNU 调试程序 gdb,它是一个用来调试 C 和 C++ 程序的调试器。可以使程序开发者在程序运行时观察程序的内部结构和内存的使用情况 .
gdb 所提供的一些主要功能如下所示:
1 运行程序,设置能影响程序运行的参数和环境 ;
2 控制程序在指定的条件下停止运行;
3 当程序停止时,可以检查程序的状态;
4 当程序 crash 时,可以检查 core 文件;
5 可以修改程序的错误,并重新运行程序;
6 可以动态监视程序中变量的值;
7 可以单步执行代码,观察程序的运行状态。
示例:
我所做的项目中一个服务进程有45个线程并行运行。发现程序异常,可以通过
pstack 进程号 查看各个线程的堆栈信息。
需要连续多次执行 pstack 进程号 命令。 查看每个线程的函数调用关系的堆栈,进行分析。当进程吊死的时候,多次使用,死锁的线程将一直处于等锁的状态,确定某些线程一直没有变化,一直处于等锁的状态。那么这些线程很可能是死锁了。如果怀疑哪些线程发生死锁了,可以采用gdb 进一步attach线程并进行分析。
如上两个图,我们发现线程18和线程1,2,3,都处于等锁状态,等待相同的锁,连续多次观察都是这个现象。
因此: 执行命令 gdb attach 9368(进程号) 进入gdb调试终端。
运行:(gdb) info thread
系统调用可能导致死锁,
syslog
system
内存释放
exit