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

blenderproc 制作位姿估计数据集

blenderproc 制作位姿估计数据集 准备工作 1.首先需要去下载blenderproc的源码blenderproc,按照作者的readme文件进行编译。 2.其次需要下载bop_toolkit源码,链接如下bop_toolkit 3.说明:blenderproc主要用来生成数据,包括depth,rgb,位姿标签文件(scene_gt.json),bop_toolkit主要用来生成mask和mask_visib,scene_gt_info.json,用于判断物体的可见区域比例,如果遮挡太多考虑去掉。做

qq_43814359的博客 5697

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

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

LearnLHC的博客 8811

K230开发板实战:5步搞定YOLOv8模型部署(附常见报错解决方案)

本文提供了一份详细的K230开发板部署YOLOv8模型的实战教程。内容涵盖从环境准备、模型转换到代码编写的完整流程,并重点解析了使用AICube工具链时常见的“生成部署文件异常”等报错解决方案,旨在帮助开发者高效完成嵌入式AI模型部署。

weixin_29053073的博客 784

手把手教你利用qemu搭建银河麒麟V11 arm环境

在Windows环境下,初学者或企业用户常通过VMware、VirtualBox等虚拟机软件安装Ubuntu、Red Hat等Linux系统,这类工具更侧重X86等复杂指令集架构(CISC,常见于Intel、AMD处理器)的虚拟化。而QEMU是一款开源的虚拟机监视器与模拟器,核心优势在于可模拟ARM、MIPS、RISC-V等精简指令集架构(RISC)的处理器并提供虚拟化能力,更适合对虚拟化技术有一定了解的用户。可能是电脑性能问题,安装过程比较慢,需要耐心等待。等操作系统下载完成后,进行安装了。

huanhuanzhou blog 2455

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

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

weixin_57672347的博客 2585

深入了解torch框架

大国的游戏,政府支持到位,是未来;AI : Artificial Intelligence,旨在研究、开发能够模拟、延伸和扩展人类智能的理论、方法、技术及应用系统,是一种拥有自主学习和推理能力的技术。它模仿人类大脑某些功能,包括感知、学习、理解、决策和问题解决。AI本质​ NLP(说话,听)、CV(眼睛)、自动驾驶、机器人(肢体动作)、大模型三要素:数据、网络、算力① 神经网络:找到合适的数学公式;② 训练:用已有数据训练网络,目标是求最优解;③ 推理:用模型预测新样本;PyTorch,简称Torch

qq_74732076的博客 1136

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

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

weixin_29607417的博客 898

C++ PCL点云处理实战专栏

工业级点云处理技术学习资源,涵盖电力、建筑、隧道等领域的实战案例。内容分为三阶段:代码原理(算法认知)、动态库(项目验证)和完整代码(定制开发)。

weixin_45196569的博客 1117

把 LLM 编排能力塞进 ABAP 系统里,ZLLM 仓库的工程价值与 SAP 落地场景

ZLLM项目将LLM应用开发中的常见工程问题引入ABAP原生环境,为SAP系统提供AI工作流构建能力。它类似ABAP版的LangChain,支持Chain组合、Prompt模板、状态传递等核心功能,强调纯ABAP实现、延迟执行以及与ABAP数据结构的深度集成。项目包含工厂类、LLM客户端、负载均衡器等模块,特别设计了能处理ABAP内表和结构的模板引擎,使业务数据可直接用于Prompt生成。通过Step和Flow机制支持复杂业务过程编排,并采用延迟执行和并行处理优化批量任务性能。该项目为ABAP开发者提供了在

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

gdb从入门到精通

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

weixin_50873490的博客 990

12、认知地图的建模与学习

本博客系统介绍了认知地图的定义、背景及其在机器智能领域的应用,重点探讨了模糊认知地图(FCMs)的建模方法与优势。文章还对比分析了几种主流的认知地图模型(如Axelrod模型、Kosko模型等),并讨论了模型的稳定性条件及实际应用前景。通过智能制造和医疗诊断的具体案例,展示了认知地图在复杂因果推理中的强大能力,并提出了模型优化方向与未来研究展望。

plant的博客 209

死锁的认识与处理

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

m0_74087691的博客 849

基于 Qt C++ 的 2D 动画制作工具

你可以修改它的中心坐标 (X, Y)、宽度 (W)、高度 (H),或点击 "选择颜色" 按钮改变其填充色。* **图形视图 (`QGraphicsView` 和 `QGraphicsScene`)**: 用于显示和编辑当前帧的内容。

寒寒豆豆软件公司的博客 114

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

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

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

A Charmer 的博客 2142

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

linux死锁检测的一种思路

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

zdy0_2004的专栏 8410

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

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、付费专栏及课程。

余额充值