Linux下排除死锁详细教程(基于C++11、GDB)

C++多线程死锁检测方法 基本思路:在加锁时,通过有向图来对锁进行记录,判断图结构是否形成了闭环,形成闭环则出现死锁。 阅读详情
AI摘要

本文通过一个C++11的多线程死锁实例,详细介绍了如何在Linux环境下使用GDB排查死锁问题。首先模拟了两个线程互相等待对方持有资源导致的死锁场景,然后利用`ps`和`top`命令观察进程状态,最后通过GDB的`attach`和`thread apply all bt`命令定位到死锁发生的线程及原因。

Linux下排除死锁详细教程(基于C++11、GDB)

1. 前言

在实际编写项目的过程中,经常涉及到多线程。多线程的程序编程很大概率会涉及到线程安全的问题,因此往往使用互斥锁来保证线程安全。

然而,互斥锁的使用却经常会导致另外一个问题:死锁。

所谓死锁,通俗的讲就是有两个共享资源,一个在你手上,一个在我手上。我等着你用完把另一个给我,你等我用完给你,这样相互等待就形成了死锁。

所以,在这里,基于Linux的环境,使用C++11提供的多线程编程来模拟死锁,并尝试从“不知情”的角度,使用shell + gbd 进行死锁的排查。

2. 模拟死锁

  1 #include <iostream>
  2 #include <thread>
  3 #include <mutex>
  4 
  5 using namespace std;
  6 
  7 mutex mtxA;
  8 mutex mtxB;
  9 
 10 void taskA() {
 11     lock_guard<mutex> lockA(mtxA);
 12     cout << "Thread A get Lock A!" << endl;
 13     this_thread::sleep_for(chrono::seconds(1));
 14     lock_guard<mutex> lockB(mtxB);
 15     cout << "Thread A get Lock A and B!" << endl;
 16     cout << "Thread A relese all source!" << endl;
 17 
 18 }
 19 
 20 void taskB() {
 21     lock_guard<mutex> lockB(mtxB);
 22     cout << "Thread B get Lock B!" << endl;
 23     this_thread::sleep_for(chrono::seconds(1));
 24     lock_guard<mutex> lockA(mtxA);
 25     cout << "Thread B get Lock B and A!" << endl;
 26     cout << "Thread B relese all source!" << endl;
 27 }
 28 
 29 int main() {
 30 
 31     thread t1(taskA);
 32     thread t2(taskB);
 33 
 34     t1.join();
 35     t2.join();
 36 
 37     return 0;
 38 }

这里我们使用了C++11中的多线程编程来模拟了一个死锁的场景。

首先,对于线程t1,会执行工作函数taskA。在taskA里面,线程t1首先会拿到 mtxA 这把互斥锁。然后睡眠1秒,让 线程t2执行工作函数taskB,由线程t2拿到 mtxB 这把互斥锁。这时,线程t1醒过来想拿锁B,而B却在t2手里。t2在拿到 mtxB 时,马上想获取 mtxA,但是在睡眠的t1手里。

因此,线程t1和t2循环等待对方的资源,又释放自己手里的资源,这样就形成了死锁。

我们将这个文件命名为:deadLock.cc ,并使用g++进行编译,生成可执行文件 (tip:注意一定要加上 -pthread,因为C++11里面的多线程库在Linux下也是基于pthread这个库实现的)::

g++ deadLock.cc -pthread -o deadLock -g

如此生成了可执行文件,因此可以运行:

./deadLock

运行结果:

在这里插入图片描述

可以看见,线程t1和t2各自获取了一把锁,同时它们都在等待对面释放手里的锁,因此造成了死锁。

3. 排查死锁

首先,我们怀疑一个程序发生了死锁,首先可以查看该进程CPU利用率、内存利用率的情况。因为如果发生了死锁(这里假设是互斥锁),进程里面发生死锁的线程会处于阻塞的状态,此时基本不占有CPU,因此CPU的利用率、内存占有率将会比较低。 我们可以使用 ps aux 命令来拿到一个进程的状态:

ps aux | grep deadLock

在这里插入图片描述

这里可以看见有两个和deadLock相关的进程:6586、6674。后面一个是执行了grep这个命令之后产生的进程,所以第一个进程是我们想排查的进程。

然后,使用 top 命令来查看进程的CPU利用率、内存占有率:

top -Hp 6586

在这里插入图片描述
可以看见,这个进程里面一共存在三个线程。仔细思考,应该是对应线程t1、t2、和 main线程。它们的CPU利用率、内存都是0,很有可能发生了死锁。

此时,进程已经运行起来了。在实际的项目中,我们一般也不可能把一个进程停掉用GDB调试。因此,只能用GDB 的 attach 命令来跟踪这个进程:

首先需要超级权限,输入密码:

su

然后:

gdb attach 6586

在这里插入图片描述

再查看三个线程的堆栈的调用情况:

thread apply all bt

在这里插入图片描述
(按 enter键)
在这里插入图片描述
(按 enter键)
在这里插入图片描述
可以看见,上面的三张图显示了三个线程的堆栈的调用情况。我们仔细观察每个线程:

在这里插入图片描述
这里红色框中有main,说明线程1是主线程。

在这里插入图片描述
这里红色框中有taskA,说明线程2是线程t1。

在这里插入图片描述
这里红色框中有taskB,说明线程3是线程t2。

因此,到此,我们知道了线程1是main线程、线程2是t1、线程3是t2。所以,下一步我们单独查看每个线程的堆栈调用情况。 使用:

info threads

得到:
在这里插入图片描述
各个线程的索引。

使用 thread + 线程索来切换到某个线程:

thread 1

在这里插入图片描述
使用 bt 来查看堆栈当前线程的堆栈调用:

在这里插入图片描述
可以大概浏览一下,当前线程没有和锁相关的调用,所以大概率死锁不发生在这个线程中。

接下来切换到线程2,再使用bt 查看堆栈:

在这里插入图片描述
在这里插入图片描述

在上面这张图中,从上往下看,找到进程名+行数的组合最后出现的地方,出现在程序的14行。我们用vim看一下程序的14行是什么:

在这里插入图片描述
刚好是线程t1醒来后想去拿锁B的行数。所以基本可以判断,这个线程拿不到锁,一直阻塞等待。

我们再查看线程3的情况 :

在这里插入图片描述
在这里插入图片描述
可以看见,线程3在执行完进程的23行基本阻塞住了,我们再去看看23行是什么:

在这里插入图片描述
可以看见,线程t2想拿锁A,但锁A在t1手里,所以它们俩循环等待对方先释放锁,造成了死锁。

至此,排除基本结束。接下来解除死锁的办法有很多,比如使用资源有序分配法,对于线程t1、t2按相同的顺序分配锁(这里线程t1先A再B,线程t2先B再A,因此死锁了),或者先把锁分配给一个线程,用完再释放全部的锁,这样也能解决死锁的问题。

【操作系统】GDB 精确定位死锁位置技巧 需要注意的是,如果程序中有多个线程同时等待同一个互斥锁,那么通过查看调用栈可能无法确定当前线程正在等待哪个互斥锁。此时,可以通过查看互斥锁的地址,以及在其他线程中调用。命令查看当前进程中所有线程的信息,包括线程 ID、状态、调用栈等信息。通过查看线程的调用栈,可以得知当前线程正在等待哪个互斥锁。找到当前正在运行的线程,并查看其调用栈。如果有,可以查看其参数,即可得知当前线程正在等待哪个互斥锁。函数的调用,可以查看其参数,即可得知当前线程正在等待哪个互斥锁。命令查看当前进程中所有线程的信息。 阅读详情

相关推荐

Linux上分析死锁的简单方法(含运行代码)

本文简单介绍了一种在 Linux 平台下分析死锁问题的方法,对一些死锁问题的分析有一定作用。希望对大家有帮助。理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和解除死锁。所以,在系统设计、进程调度等方面注意如何不让这四个必要条件成立,如何确定资源的合理分配算法,避免进程永久占据系统资源。

j简说Linux的博客 981

Linux GDB调试死锁问题

1.死锁介绍 1.1 锁的简介 由于多线程的模式下,各个线程并发运行(注意“并发和“并行”的区别),为了保证各个线程对公共资源的访问时出现数据不一致性的问题,出现了锁的机制。 Linux系统编程中最常见的锁机制是通过互斥量(mutex)来实现的。任一时刻只有一个线程可以对互斥量mutex上锁(或说成持有该互斥量),在被持有期间,其它线程就无法对它进行上锁(这也是互斥量名字中“互斥”的由来),其它尝试加锁的线程都会休眠(注意休眠和挂起的区别)释放cpu资源,并被记录到此互斥量的等待队列中。持有的线程释.

weixin_38331755的博客 3546

get_seed协议

【代码】get_seed协议。

a545958498的博客 911

C++如何判断一个程序是 死锁 还是 死循环,如何进行问题定位与分析

目录 一、初步诊断: 1、死循环: 2、死锁: 二、进一步定位分析: 1、初步诊断为 死循环 时,可转存为 Dump 进行分析 2、死锁 情况就比较复杂: 三、、代码与图示: 1、死循环: 2、死锁: 一、初步诊断: 通过 ‘任务管理器’ 查看软件的状态 与 CPU 使用情况 1、死循环: 软件状态:未响应 CPU:一直保持非0,处于活跃状态 原理: 如果主线程出现死循环,那么windows将不能从消息队列中取出消息,并进行处理,所以出现卡死现象。为了验证是这个原因导致

LearnLHC的博客 8811

深度可分离卷积(DepthwiseSeparableConvolution):Depthwise卷积与Pointwise卷积

深度可分离卷积不用多说,在轻量级网络架构方面是一个绕不开的话题,只要接触深度学习多多少少会接触。深度可分离卷积即DepthwiseSeparableConvolution,该卷积将一个常规卷积过程划分成两个完成Depthwise卷积和Pointwise卷积,在保证输出一样时,计算量大大降低。首先来看,一个常规卷积是怎么实现的。其示意图如果我们从通道数变换的角度来看,卷积就是将3通道的tensor变为了4通道的tensor,我们来看看深度可分离卷积是怎么实现通道的变化的。...

xu.hyj 1万+

WKWebView 白屏问题,WKWebView request body丢失问题

1,WKWebView 白屏问题 WKWebView 自诩拥有更快的加载速度,更低的内存占用,但实际上 WKWebView 是一个多进程组件,Network Loading 以及 UI Rendering 在其它进程中执行。初次适配 WKWebView 的时候,我们也惊讶于打开 WKWebView 后,App 进程内存消耗反而大幅下降,但是仔细观察会发现,Other Process 的内存占用会...

weixin_37543822的博客 736

死锁是什么?如何避免?如何排查?为什么这样排查 详细总结

1.定位线程+代码行 需要找到哪行代码发生了死锁 因此需要找到出问题的线程thread 1切换线程bt查看线程的堆栈调用)2.全部线程 那么就需要先查看所有线程的堆栈调用情况3.跟踪进程 使用attach跟踪进程sudogbd进程号确定其中线程具体工作4.确定是否死锁查看具体进程内存占用 确定是否发生了死锁5.查看进程状态 确定进程号ps。

weixin_57672347的博客 2585

电源完整性之Cadence Sigrity Power DC_IR_Drop仿真

之前和大家分享过电源完整性之仿真设计原理链接: link.今天接着上一篇文章总结一下电源直流压降的的仿真操作流程及一些simulation中的设置参数,用到的时候Cadence Sigrity 中的Power DC组件,下面开始享受仿真的快乐吧: 1.仿真模式选择与文件导入 选择Cadence Sigrity下的Power DC组件,然后先择licence,最后就可以打开我们之前转化好的.SPD文件。操作如下图: 2.设置层叠信息和过孔焊盘参数 层叠信息:每层的介质厚度、材料、介电常数、介质损耗角等,如

刘小同学的博客 1万+

linux 多线程死锁排查,linux死锁检测的一种思路

前言:上一篇博文讲述了pstack的使用和原理. 和jstack一样, pstack能获取进程的线程堆栈快照, 方便检验和性能评估. 但jstack功能更加的强大, 它能对潜在的死锁予以提示, 而pstack只提供了线索, 需要gdb进一步的确定.那Linux下, 如何去检测死锁, 如何让死锁的检测能够更加的智能和方便? 这是本文的核心主旨, 让我们一同分享下思路.常规做法:我们来模拟一个出现死锁...

weixin_29607417的博客 898

(详细教程)opencv+pycharm+笔记本摄像头 做一个人脸识别

文章目录 前言 一、pandas是什么? 二、使用步骤 1.引入库 2.读入数据 总结 一、安装opencv-python 这里推荐使用pycharm直接安装opencv-python 打开文件——设置——python解释器——点击+号 搜索opencv-python,点击安装包,等待片刻即可安装好 检查是否安装好,新建一个项目输入以下代码,若无报错则安装好 import cv2 二、准备分类器 咱们使用opencv官方提供的分类器,包括面部识别,侧...

qq_52852432的博客 1万+

DataGrip 2023.3 Windows版激活保姆级教程:从下载脚本到输入激活码,一次搞定

本文提供DataGrip 2023.3 Windows版的完整安装与授权指南,从环境准备到数据库连接管理,再到高效开发技巧与高级功能扩展,帮助开发者快速掌握这一专业数据库管理工具。内容涵盖安装步骤、初始配置、常见问题解决及性能调优技巧,适合数据库开发新手和专业人士。

weixin_30275415的博客 637

gdb从入门到精通

安装/升级gdb调试基本命令开始调试程序 -g编译 gdb ./exe。

weixin_50873490的博客 990

Waymo数据集里的隐藏彩蛋:5个激光雷达数据融合实战(附Python代码)

本文深入解析Waymo数据集中5个激光雷达的数据融合技术,提供Python实战代码,帮助开发者掌握多传感器协同工作的关键方法。通过时间同步、坐标转换和运动补偿等技术,实现高精度3D场景重建,提升自动驾驶系统的环境感知能力。

weixin_30399821的博客 80

死锁的认识与处理

简单来说,死锁问题的产生是由两个或者以上线程并行执行的时候,争夺资源而互相等待造成的。死锁只有同时满足互斥、持有并等待、不可剥夺、环路等待这四个条件的时候才会发生。所以要避免死锁问题,就是要破坏其中一个条件即可,最常用的方法就是使用资源有序分配法来破坏环路等待条件。

m0_74087691的博客 849

读懂 SAP Fiori Administration Catalogs:从管理员入口、权限编排到 Spaces 和 Pages 的落地实践

SAP Fiori 管理员目录的核心价值在于为系统配置提供标准化入口,解决权限与展示分离的问题。随着 SAP_UI 7.55 版本更新,SAP_FLP_ADMIN 角色整合了关键管理工具(如 Launchpad Designer、Content Manager),并通过 Business Catalog 和 Space 的协同作用,确保管理员既能访问配置工具,又能直观查看相关功能入口。项目实施中需注意配置层级(系统级/客户端级)的选择,以及多人协作时的数据同步问题,以提升交付效率。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 194

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

内存泄漏、死锁:定位排查工具+解决方案(C/C++ 实战指南)

本文针对C/C++开发中的内存泄漏和死锁问题,提供了从定位到解决的全流程实战指南。对于内存泄漏,介绍了Valgrind、AddressSanitizer等工具的用法,分析了常见泄漏场景及智能指针、RAII等解决方案。对于死锁问题,详细讲解了pstack+gdb、ThreadSanitizer等诊断工具的使用方法,并给出预防死锁的编码规范。文章覆盖Linux/Windows平台,包含基础判断方法、工具使用示例和典型问题解决方案,适用于从开发调试到生产环境的各种场景。

A Charmer 的博客 2142

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

linux死锁检测的一种思路

linux死锁检测的一种思路 http://www.cnblogs.com/mumuxinfei/p/4365697.html 前言:    上一篇博文讲述了pstack的使用和原理. 和jstack一样, pstack能获取进程的线程堆栈快照, 方便检验和性能评估. 但jstack功能更加的强大, 它能对潜在的死锁予以提示, 而pstack只提供了线索, 需要gdb进一步的

zdy0_2004的专栏 8410

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

GDB 调试与 Core Dump 分析技术

GDB(GNU Debugger)是 Linux/Unix 系统中最常用的程序调试工具,可用于 C/C++、汇编等语言的程序调试,能帮助开发者定位运行时错误、查看内存状态、跟踪程序执行流程。GDB 调试与 Core Dump 分析是软件开发中定位运行时错误的核心技术,前者用于动态调试,后者用于事后分析,两者结合可高效解决复杂的程序崩溃问题。Core Dump 是程序崩溃时系统生成的内存镜像文件,记录了进程崩溃时的内存状态、寄存器值、调用栈等信息,可用于事后分析错误原因。:调试一个段错误程序。

qq2745567641的博客 1276

C++多线程死锁检测实战:6大技巧构建可观测防御体系

在多线程编程中,死锁是导致系统停滞的经典并发问题,其本质是多个线程因循环等待资源而陷入永久阻塞。理解死锁的原理需要从资源分配图(RAG)和锁的互斥性入手,它直接关系到系统的稳定性和可靠性。在C++高级开发中,死锁检测不仅是理论概念,更是保障高并发服务(如游戏服务器、实时数据处理引擎)稳定运行的核心工程实践。通过构建多层次防御体系,结合静态分析工具(如Clang-Tidy)进行编译期预警,并实现运行时锁依赖图追踪,开发者可以主动发现并规避潜在的循环等待风险。这些技术最终应用于构建可观测的系统,使死锁从不可控的

weixin_30571465的博客 332

linux死锁检测的一种思路【转】

转自:http://blog.youkuaiyun.com/zdy0_2004/article/details/44652323 linux死锁检测的一种思路 http://www.cnblogs.com/mumuxinfei/p/4365697.html 前言:   上一篇博文讲述了pstack的使用和原理. 和jstack一样, pstack能获取进程的线程堆栈快照, 方便检验和性能评...

weixin_34220834的博客 72

C++调试指南说明

C++ 调试指南,gdb调试,检测内存泄漏,崩溃排查

weixin_43803635的博客 991

为什么调试很重要?gdb调试分析问题

调试是指通过对程序、硬件或系统的错误进行定位、分析和修复的过程。调试可以帮助开发人员发现并解决程序中的逻辑错误、语法错误、运行时错误等问题,保证软件的正确性和可靠性。在调试过程中,开发人员需要使用各种调试工具和技术,如日志记录、断点调试、边界测试等,以便更快速地定位和解决问题。调试是软件开发过程中不可或缺的重要环节。调试是软件开发过程中必不可少的环节,只有通过有效的调试才能在保证质量的同时提高开发效率。需要掌握正确的调试方法和技巧,避免滥用调试工具造成安全风险。

Lion_Long的博客 1086
上一篇: 设计模式:装饰器模式(C++实现)
下一篇: 解决:Linux下C++段错误不产生core文件
zsiming
博客等级 码龄7年 43粉丝 74原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值