目录
测试案例
// deadlock.cpp
#include <stdio.h>
#include <pthread.h>
#include "iostream"
pthread_mutex_t m1 ;
pthread_mutex_t m2 ;
void *thread_1(void*) {
pthread_mutex_lock(&m1);
std::cout << "thread_1" << std::endl;
pthread_mutex_lock(&m2);
pthread_mutex_unlock(&m1);
pthread_mutex_unlock(&m2);
return nullptr;
}
void *thread_2(void*) {
pthread_mutex_lock(&m2);
std::cout << "thread_2" << std::endl;
pthread_mutex_lock(&m1);
pthread_mutex_unlock(&m2);
pthread_mutex_unlock(&m1);
return nullptr;
}
int main()
{
int ret=0;
pthread_t tid1, tid2;
ret = pthread_create(&tid1, NULL, thread_1, NULL);
if (ret)
{
printf("Create pthread error!/n");
return 1;
}
ret = pthread_create(&tid2, NULL, thread_2, NULL);
if (ret)
{
printf("Create pthread error!/n");
return 1;
}
pthread_join(id1, NULL);
pthread_join(id2, NULL);
return 0;
}
编译方式
g++ -std=c++11 -g deadlock.cpp -lpthread
现象
多次执行发现整个进程有卡住现象
准备工具
一、PS 概述
Linux中的PS命令是Process Status的缩写。ps命令用来列出系统中当前运行的那些进程。ps命令列出的是当前那些进程的快照,就是执行ps命令的那个时刻的那些进程,如果想要动态的显示进程信息,就可以使用top命令。
PS的用法
ps [ 参数 ]
参数:
a:显示现行终端机下的所有进程,包括其他用户的进程;
u:显示进程拥有者、状态、资源占用等的详细信息;
x:显示没有控制终端的进程。通常与 a 这个参数一起使用,可列出较完整信息;
-e:显示所有进程;
-f:完整输出显示进程之间的父子关系;
-l:较长、较详细的将该 PID 的的信息列出;
-o:自定义显示的字段;
参数还有很多,就不一一列举了。详细信息请在linux服务器上面执行下面的命令
man ps
二、gcore工具
gcore的用法
gcore [pid]
三、gdb工具
在linux程序调成中gdb是最常用的工具。
定位步骤
一般卡住问题都需要保留现场环境,不影响业务,重启进程,然后定位分析原因。
一、保留环境
1. 查看进程pid
ps -ef | grep a.out
通过ps命令可以看出,出现卡住问题的进程的PID为1691。
2. 使用gcore保留当前环境
gcore 1691
通过上面命令可以看到,产生了一个叫做core.1691的coredump文件。
二、分析Core文件
1. 分析core文件找到死锁原因
<1> 先把core文件打开
gdb ../code/deadlock/t1/a.out core.1691
<2> 显示所有线程信息
info threads
通过这个命令可以分析出进程中有三个线程再跑。两个线程都停留在__lll_lock_wait函数处,一个停留在pthread_join函数处。
GDB会为每一个线程分配一个线程ID,例如
thread Id为1 对应的LWP为 1692
thread Id为2 对应的LWP为 1693
thread Id为3 对应的LWP为 1691
pthread_join函数的通常是等待指定线程ID的线程执行完毕。如果指定线程ID的线程没有执行完,停留在pthread_join处是合理的。那么考虑是不是创建出来的线程没有执行完毕导致的问题。则进一步分析。
<3> 查看指定线程ID的堆栈信息
t 1
通过上述可以分析出ID为1的线程的 LWP为1692,这里说明下LWP是什么?LWP称作轻量级进程,可见其本质仍然是进程,与普通进程相比,LWP与其它进程共享所有(或大部分)逻辑地址空间和系统资源,一个进程可以创建多个LWP,这样它们共享大部分资源;LWP有它自己的进程标识符,并和其他进程有着父子关系;这是和类Unix操作系统的系统调用vfork()生成的进程一样的。LWP由内核管理并像普通进程一样被调度。
可以看到栈帧#3为我们自己写的代码。查看具体信息
查看源码信息
通过源码可知,thread id 为 1 的thread_1函数中,函数先持有m1(互斥量),然后想获取m2(互斥量)。
根据pthread api 可知pthread_mutex_lock的参数是一个互斥量。目前卡住在pthread_mutex_lock函数中,可以分析出pthread_mutex_lock想到获取一个互斥量,但是这个互斥量被别人持有导致一直获取不到,所以卡住在这里了。
那么查看这个互斥量被谁持有呢,我们打印下这个互斥量通过下面的命令。
可以看到这个互斥量被1693线程持有,通过上面的分析可知thread id 为2 的LWP 为1693
我们查看thread id 为2线程信息
通过上述分析可知
thread id 为 1 LWP(1692)先持有m1(互斥量),然后想获取m2(互斥量), 但是m2被LWP(1693)持有。
thread id 为 2 LWP(1693)先持有m2(互斥量),然后想获取m1(互斥量), 但是m1被LWP(1692)持有。
上述情况导致死锁, 两个线程都停不下来。所以thread id 为 3的线程等待他们结束, 因为他们没有结束所以也卡住了。
结论
此demo是简单死锁的情况,现实环境往往比这个复杂的多, 但是重要的是要学会看互斥量被谁持有和一些简单的gdb命令。

微信公众号名称:技术茶馆
微信公众号ID : Night_ZW