【线上问题定位】简单死锁问题定位

目录

测试案例

现象

准备工具

一、PS 概述

二、gcore工具

三、gdb工具

定位步骤

一、保留环境

二、分析Core文件

结论


测试案例

// 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值