用gdb调试死锁的问题。

首先给出有死锁问题的代码
g++ -g -lpthread lock.cpp -o lock
lock.cpp

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono> // 添加此头文件以支持 std::chrono::milliseconds

using std::cout;
using std::mutex;
std::mutex mutexA, mutexB;
using std::lock_guard;
using std::this_thread::sleep_for; // 使用 sleep_for 而不是整个 this_thread 命名空间

/*
有两个线程 Thread 1 和 Thread 2,它们尝试获取 mutexA 和 mutexB,但由于加锁顺序不同,导致死锁。
 * */
void thread1()
{
    lock_guard<mutex> lockA(mutexA); // 先获取 mutexA
    sleep_for(std::chrono::milliseconds(100)); // 模拟线程执行时间
    lock_guard<mutex> lockB(mutexB); // 再获取 mutexB
    cout << "Thread 1: got mutexB" << std::endl; // 这行代码不会被执行
}

// 线程2
void thread2()
{
    lock_guard<mutex> lockB(mutexB); // 先获取 mutexB
    sleep_for(std::chrono::milliseconds(100)); // 模拟线程执行时间
    lock_guard<mutex> lockA(mutexA); // 再获取 mutexA
    cout << "Thread 2: got mutexA" << std::endl; // 这行代码不会被执行
}

int main()
{
    std::thread t1(thread1);
    std::thread t2(thread2);

    t1.join();
    t2.join();

    return 0;
}

thread1 首先获取 mutexA,然后尝试获取 mutexB。
thread2 首先获取 mutexB,然后尝试获取 mutexA。
如果 thread1 成功获取了 mutexA,并且在等待 mutexB 的过程中,thread2 成功获取了 mutexB 并等待 mutexA,那么 thread1 和 thread2 将永远等待对方释放互斥锁,从而形成死锁。

以下是使用gdb调试该死锁程序的具体步骤:

  1. 编译带调试信息的程序
g++ -g lock.cpp -o lock -lpthread
  1. 启动gdb调试
gdb ./lock
  1. 设置关键断点(在gdb提示符下)
b thread1   # 在thread1入口设置断点
b thread2   # 在thread2入口设置断点
b lock.cpp:18 # 在获取mutexB的位置设置断点
b lock.cpp:30 # 在获取mutexA的位置设置断点
  1. 运行程序
run
  1. 当程序出现死锁时(不会正常退出),按Ctrl+C中断程序

  2. 查看线程状态

info threads
  1. 切换到线程1查看堆栈
thread 1
bt
  1. 切换到线程2查看堆栈
thread 2
bt
  1. 分析死锁状态:
  • 检查每个线程持有的锁:
print mutexA   # 查看mutexA状态(持有者线程ID)
print mutexB   # 查看mutexB状态(持有者线程ID)
  1. 调试输出分析:
  • 当两个线程分别卡在:
    thread1卡在lock.cpp:18(等待mutexB)
    thread2卡在lock.cpp:30(等待mutexA)
  • 同时通过print命令确认:
    mutexA被thread1持有
    mutexB被thread2持有
  1. 强制退出调试:
quit

调试结果解读:

  1. 线程1的调用栈显示它卡在获取mutexB的调用上
  2. 线程2的调用栈显示它卡在获取mutexA的调用上
  3. 通过互斥量状态可以确认:
    • 线程1持有mutexA,请求mutexB
    • 线程2持有mutexB,请求mutexA
  4. 形成循环等待,导致死锁

解决方案:

  1. 统一锁的获取顺序(两个线程都先获取mutexA再获取mutexB)
  2. 使用std::lock()原子地获取多个锁:
std::lock(mutexA, mutexB);
std::lock_guard<std::mutex> lockA(mutexA, std::adopt_lock);
std::lock_guard<std::mutex> lockB(mutexB, std::adopt_lock);

具体信息解读

(gdb) b thread1   # 在thread1入口设置断点
b thread2   # 在thread2入口设置断点
b lock.cpp:18 # 在获取mutexB的位置设置断点
b lock.cpp:30 # 在获取mutexA的位置设置断点
No source file named thread1   # 在thread1入口设置断点
b thread2   # 在thread2入口设置断点
b lock.cpp.
# 当你在使用gdb调试程序并尝试设置断点时,提示“Make breakpoint pending on future shared library load? (y or [n])”是因为你设置的断点所在的函数或代码片段属于动态加载的共享库(shared library),而该共享库尚未加载。
Make breakpoint pending on future shared library load? (y or [n]) n

(gdb) r
Starting program: /home/haonan/share/testCode/test_bug/lock 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff7600640 (LWP 15181)]
[New Thread 0x7ffff6c00640 (LWP 15182)]
^C
Thread 1 "lock" received signal SIGINT, Interrupt.
__futex_abstimed_wait_common64 (private=128, cancel=true, abstime=0x0, op=265, expected=15181, futex_word=0x7ffff7600910) at ./nptl/futex-internal.c:57
57      ./nptl/futex-internal.c: No such file or directory.

(gdb) info threads
  Id   Target Id                                Frame 
  # main
* 1    Thread 0x7ffff7ea23c0 (LWP 15178) "lock" __futex_abstimed_wait_common64 (private=128, cancel=true, abstime=0x0, op=265, 
    expected=15181, futex_word=0x7ffff7600910) at ./nptl/futex-internal.c:57
  # thread1() 卡在了mutex
  2    Thread 0x7ffff7600640 (LWP 15181) "lock" futex_wait (private=0, expected=2, futex_word=0x55555555a1a0 <mutex>)
    at ../sysdeps/nptl/futex-internal.h:146
  # thread2() 卡在了mutexA
  3    Thread 0x7ffff6c00640 (LWP 15182) "lock" futex_wait (private=0, expected=2, futex_word=0x55555555a160 <mutexA>)
    at ../sysdeps/nptl/futex-internal.h:146
 # 切换到线程2
(gdb) thread 2
[Switching to thread 2 (Thread 0x7ffff7600640 (LWP 15181))]
#0  futex_wait (private=0, expected=2, futex_word=0x55555555a1a0 <mutex>) at ../sysdeps/nptl/futex-internal.h:146
146     ../sysdeps/nptl/futex-internal.h: No such file or directory.
(gdb) bt
#0  futex_wait (private=0, expected=2, futex_word=0x55555555a1a0 <mutex>) at ../sysdeps/nptl/futex-internal.h:146
#1  __GI___lll_lock_wait (futex=futex@entry=0x55555555a1a0 <mutex>, private=0) at ./nptl/lowlevellock.c:49
#2  0x00007ffff7898002 in lll_mutex_lock_optimized (mutex=0x55555555a1a0 <mutex>) at ./nptl/pthread_mutex_lock.c:48
#3  ___pthread_mutex_lock (mutex=0x55555555a1a0 <mutex>) at ./nptl/pthread_mutex_lock.c:93
#4  0x00005555555556b7 in __gthread_mutex_lock (__mutex=0x55555555a1a0 <mutex>)
    at /usr/include/x86_64-linux-gnu/c++/11/bits/gthr-default.h:749
#5  0x000055555555582e in std::mutex::lock (this=0x55555555a1a0 <mutex>) at /usr/include/c++/11/bits/std_mutex.h:100
#6  0x00005555555558a8 in std::lock_guard<std::mutex>::lock_guard (this=0x7ffff75ffd70, __m=...)
    at /usr/include/c++/11/bits/std_mutex.h:229
# ---------------------------------------------------------------    
# thread1() 在 lock.cpp:19 处,尝试获取 mutex(假设是 mutexA)。
# 但它被阻塞,说明 mutexA 已被其他线程持有。
#7  0x00005555555553c2 in thread1 () at lock.cpp:19
#8  0x0000555555556487 in std::__invoke_impl<void, void (*)()> (__f=@0x55555556ceb8: 0x555555555354 <thread1()>)
    at /usr/include/c++/11/bits/invoke.h:61
#9  0x0000555555556433 in std::__invoke<void (*)()> (__fn=@0x55555556ceb8: 0x555555555354 <thread1()>)
    at /usr/include/c++/11/bits/invoke.h:96
#10 0x00005555555563d4 in std::thread::_Invoker<std::tuple<void (*)()> >::_M_invoke<0ul> (this=0x55555556ceb8)
    at /usr/include/c++/11/bits/std_thread.h:259
#11 0x00005555555563a4 in std::thread::_Invoker<std::tuple<void (*)()> >::operator() (this=0x55555556ceb8)
    at /usr/include/c++/11/bits/std_thread.h:266
--Type <RET> for more, q to quit, c to continue without paging--
#12 0x0000555555556384 in std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)()> > >::_M_run (this=0x55555556ceb0)
    at /usr/include/c++/11/bits/std_thread.h:211
#13 0x00007ffff7cdc253 in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#14 0x00007ffff7894ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#15 0x00007ffff7926850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
(gdb) 
(gdb) thread 1
[Switching to thread 1 (Thread 0x7ffff7ea23c0 (LWP 15178))]
#0  __futex_abstimed_wait_common64 (private=128, cancel=true, abstime=0x0, op=265, expected=15181, futex_word=0x7ffff7600910)
    at ./nptl/futex-internal.c:57
57      ./nptl/futex-internal.c: No such file or directory.
(gdb) bt
#0  __futex_abstimed_wait_common64 (private=128, cancel=true, abstime=0x0, op=265, expected=15181, futex_word=0x7ffff7600910)
    at ./nptl/futex-internal.c:57
#1  __futex_abstimed_wait_common (cancel=true, private=128, abstime=0x0, clockid=0, expected=15181, futex_word=0x7ffff7600910)
    at ./nptl/futex-internal.c:87
#2  __GI___futex_abstimed_wait_cancelable64 (futex_word=futex_word@entry=0x7ffff7600910, expected=15181, clockid=clockid@entry=0, 
    abstime=abstime@entry=0x0, private=private@entry=128) at ./nptl/futex-internal.c:139
#3  0x00007ffff7896624 in __pthread_clockjoin_ex (threadid=140737343653440, thread_return=0x0, clockid=0, abstime=0x0, 
    block=<optimized out>) at ./nptl/pthread_join_common.c:105
#4  0x00007ffff7cdc2c7 in std::thread::join() () from /lib/x86_64-linux-gnu/libstdc++.so.6
#5  0x00005555555555a8 in main () at lock.cpp:37
# 切换到线程3
(gdb) thread 3
[Switching to thread 3 (Thread 0x7ffff6c00640 (LWP 15182))]
#0  futex_wait (private=0, expected=2, futex_word=0x55555555a160 <mutexA>) at ../sysdeps/nptl/futex-internal.h:146
146     ../sysdeps/nptl/futex-internal.h: No such file or directory.
(gdb) bt
#0  futex_wait (private=0, expected=2, futex_word=0x55555555a160 <mutexA>) at ../sysdeps/nptl/futex-internal.h:146
#1  __GI___lll_lock_wait (futex=futex@entry=0x55555555a160 <mutexA>, private=0) at ./nptl/lowlevellock.c:49
#2  0x00007ffff7898002 in lll_mutex_lock_optimized (mutex=0x55555555a160 <mutexA>) at ./nptl/pthread_mutex_lock.c:48
#3  ___pthread_mutex_lock (mutex=0x55555555a160 <mutexA>) at ./nptl/pthread_mutex_lock.c:93
#4  0x00005555555556b7 in __gthread_mutex_lock (__mutex=0x55555555a160 <mutexA>)
    at /usr/include/x86_64-linux-gnu/c++/11/bits/gthr-default.h:749
#5  0x000055555555582e in std::mutex::lock (this=0x55555555a160 <mutexA>) at /usr/include/c++/11/bits/std_mutex.h:100
#6  0x00005555555558a8 in std::lock_guard<std::mutex>::lock_guard (this=0x7ffff6bffd70, __m=...)
    at /usr/include/c++/11/bits/std_mutex.h:229
# -----------------------------------------------------------------
# thread2() 在 lock.cpp:28 处,尝试获取 mutexA。
# 但 mutexA 已被 thread1() 持有,而 thread1() 在等另一个 mutex。
#7  0x00005555555554c2 in thread2 () at lock.cpp:28
#8  0x0000555555556487 in std::__invoke_impl<void, void (*)()> (__f=@0x55555556d008: 0x555555555454 <thread2()>)
    at /usr/include/c++/11/bits/invoke.h:61
#9  0x0000555555556433 in std::__invoke<void (*)()> (__fn=@0x55555556d008: 0x555555555454 <thread2()>)
    at /usr/include/c++/11/bits/invoke.h:96
#10 0x00005555555563d4 in std::thread::_Invoker<std::tuple<void (*)()> >::_M_invoke<0ul> (this=0x55555556d008)
    at /usr/include/c++/11/bits/std_thread.h:259
#11 0x00005555555563a4 in std::thread::_Invoker<std::tuple<void (*)()> >::operator() (this=0x55555556d008)
    at /usr/include/c++/11/bits/std_thread.h:266
#12 0x0000555555556384 in std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)()> > >::_M_run (this=0x55555556d000)
    at /usr/include/c++/11/bits/std_thread.h:211
#13 0x00007ffff7cdc253 in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#14 0x00007ffff7894ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
--Type <RET> for more, q to quit, c to continue without paging--
#15 0x00007ffff7926850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

当你在使用gdb调试程序并尝试设置断点时,提示“Make breakpoint pending on future shared library load? (y or [n])”是因为你设置的断点所在的函数或代码片段属于动态加载的共享库(shared library),而该共享库尚未加载。

以下是详细的中文解释和选择指南:

问题的含义:
共享库(Shared Library) 是一种动态加载的库,通常在程序运行过程中才加载。

当你设置断点时,如果共享库还未加载,gdb无法立即将断点绑定到代码中,因此会询问你是否希望将断点设置为“pending”(挂起状态)。

选项说明:
输入y(yes):

  • 设置“挂起的断点”,即断点暂时等待,直到共享库加载后自动激活。
  • 如果你确定共享库会在程序运行过程中加载,请选择y。

输入n(no):

  • 不设置断点。你需要在共享库实际加载后再手动设置断点。
  • 如果你不确定共享库是否会加载,或者需要立即检查其他部分代码,则选择n。

如何选择:

  • 如果你调试的目标是共享库中的代码,选择y。
    • 例如,程序运行过程中会加载共享库,并且你希望调试其中的函数,可以选择y。
  • 如果你只是调试主程序或其他部分代码,选择n。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值