linux下多线程死锁调试
多线程编程是一直多比较难的一个部分, 今天我就来介绍一下在Linux下调试c++程序死锁的一个简单方式,环境是Ubuntu16.04, gcc版本是4.9.3,gdb版本是7.11.1
获得死锁程序崩溃后的core文件
ulimit -c
如果上面的输出是0,那么说明系统目前是无法生成core文件的,因为限制了core文件的大小是0,所以自然是无法生成core文件的, 所以我们需要改变这个限制, 命令如下:
ulimit -c unlimited
这个命令设置core文件大小不受限制,因此core文件就可以正常产生.
kill -11 pid
值得注意的是我们使用的是-11选项, 前面我们提到过死锁的程序实际上是不会退出的, 因此我们就需要手动来杀死程序, 但是不能让程序直接退出, 必须要"留下点东西"下来, 也就是我们想要的core文件.
实际上我们使用
kill -l
可以看到11对应的是SIGSEGV的信号,也就是我们一般在看到Segmentation fault (core dumped)之前进程收到的信号,也就说实际上进程收到的信号决定了进程产生core dumped文件.我们使用之前说的 -11 选项, 进程就会退出并且产生core dumped文件,这个时候我们就能获得我们"梦寐以求"(笑)的core文件了.
使用gdb来调试core文件
这里我手动给出一个死锁的例子,代码如下
#include <iostream>
#include <mutex>
#include <thread>
#include <unistd.h>
using namespace std;
mutex m1, m2;
void thread1() {
lock_guard< mutex > guard( m1 );
cout << "thread1 lock mutex1" << endl;
std::cout << "then try to lock mutex2" << std::endl;
lock_guard< mutex > guard2( m2 );
void thread2() {
lock_guard< mutex > guard( m2 );
std::cout << "thread2 locked mutex2" << std::endl;
std::cout << "then sleep for 1 seconds and try to lock mutex1" << std::endl;
sleep( 1 );
lock_guard< mutex > guard2( m1 );
int main( int argc, char const* argv[] ) {
thread t1( thread1 );
thread t2( thread2 );
t1.join();
t2.join();
return 0;
然后编译运行, 这个比较简单,给出命令
g++ -std=c++11 test_dead_lock.cpp -o test_dead_lock -g -pthread
我们会获得可执行文件, 然后我们运行可执行文件,就会发现程序果然死锁了,这个时候我们不管做什么, 程序都已经失去效应, 这个时候我们需要做的就是按照步骤1的杀掉死锁进程了, 先找到进程先
ps -ef | grep test_dead_lock | grep -v grep | awk '{print $2}'
获得死锁进程的pid, 这个时候我们向这个进程发送SIGSECV信号, 也就是
kill -11 pid
进程退出, 我们可以看到熟悉的Segmentation fault (core dumped), 然后core文件也会随之产生,我这里产生的core文件名称就是core, 然后我们开始操作(笑), gdb调试开始
gdb test_dead_lock core
thread apply all bt
下面我们来看看结果:
上面比较有趣的地方有两个:
第一个是gdb调试指令,我们这次并没有用bt和info stack, 原因很简单, 那两个命令都只能查看目前正在运行的某个线程的栈信息, 这个对于我们来说是远远不够的, 而thread apply all命令, gdb会让对线程都执行这个命令, 就比如bt, 就是查看所有线程的具体栈信息.
第二个是我们使用thread apply all bt命令之后, 两个子线程的调用过程, 可以看到创建线程的系统是clone, 也就是说在Linux系统下, 操作系统用的是和产生新进程的系统调用来创建新的线程的, 按照操作系统的书籍来说的, Linux系统下创建新的线程用的就是clone, 但传递给clone的参数是和创建新的进程不同的, 比如创建新的线程的时候肯定是要共享地址空间的, 另外还要共享打开文件描述符等等,这里就不再细说, 具体可以看看操作系统相关的书籍.
写到这里就差不多了, 多线程编程一直是在c++非常难的部分,这些需要多coding和多reading才能有所提高, 希望和大家一起提高!