gdb多线程调试
2015-04-03 16:48 youxin 阅读(2645) 评论(0) 编辑 收藏 举报死锁:一种情形,此时执行程序中两个或多个线程发生永久堵塞(等待),每个线程都在等待被
有序资源分配法
是操作系统中预防死锁的一种算法,这种算法资源按某种规则系统中的所有资源统一编号(例如打印机为1、磁带机为2、磁盘为3、等等),申请时必须以上升的次序。
系统要求申请进程:
1、对它所必须使用的而且属于同一类的所有资源,必须一次申请完;
2、在申请不同类资源时,必须按各类设备的编号依次申请。
例如:进程PA,使用资源的顺序是R1,R2;
进程PB,使用资源的顺序是R2,R1;
若采用动态分配有可能形成环路条件,造成死锁。
采用有序资源分配法:R1的编号为1,R2的编号为2;
PA:申请次序应是:R1,R2
PB:申请次序应是:R1,R2
这样就破坏了环路条件,避免了死锁的发生。
另外,还有死锁避免,死锁检测与恢复等。
银行家算法
/** 死锁调试 1) -g参数 2) attach 3) info threads 4) thread + number切换到对应的线程或thread apply all bt全部设置断点 */ #include <stdio.h> #include <pthread.h> #include <unistd.h> void *workThread( void *arg ) { pthread_mutex_t mutex; pthread_mutex_init(&mutex, 0); usleep(1000*1000); fprintf(stderr,"timeout we will start dead lock\n"); pthread_mutex_lock(&mutex); pthread_mutex_lock(&mutex); } void *AliveThread ( void * arg ) { while ( true ) { usleep(1000*1000); } } int main(int argc, char *argv[]) { pthread_t alivepid; pthread_create(&alivepid,0,AliveThread,0); pthread_t deadpid; pthread_create(&deadpid, 0, workThread, 0); void *retval = 0; pthread_join(deadpid, &retval); void *retval2 = 0; pthread_join(alivepid,&retval2); return 0; }
2.编译运行 lock.c
[root@localhost ~]# gcc -g lock.c -pthread
[root@localhost ~]# ./a.out
timeout we will start dead lock
(程序挂起)
3.查找进程id
[root@localhost ~]# ps -e | grep a.out
12826 pts/3 00:00:00 a.out //进程id为12826
gdb多线程调试命令:
(gdb)info threads
显示当前可调试的所有线程,每个线程会有一个GDB为其分配的ID,后面操作线程的时候会用到这个ID。
前面有*的是当前调试的线程。
(gdb)thread ID
切换当前调试的线程为指定ID的线程。
(gdb)thread apply ID1 ID2 command
让一个或者多个线程执行GDB命令command。
(gdb)thread apply all command
让所有被调试线程执行GDB命令command。
(gdb)set scheduler-locking off|on|step
估计是实际使用过多线程调试的人都可以发现,在使用step或者continue命令调试当前被调试线程的时候,其他线程也是同时执行的,怎么只让被调试程序执行呢?通过这个命令就可以实现这个需求。
off 不锁定任何线程,也就是所有线程都执行,这是默认值。
on 只有当前被调试程序会执行。
step 在单步的时候,除了next过一个函数的情况(熟悉情况的人可能知道,这其实是一个设置断点然后continue的行为)以外,只有当前线程会执行。
//显示线程堆栈信息
(gdb) bt
察看所有的调用栈
(gdb) f 3
调用框层次
(gdb) i locals
显示所有当前调用栈的所有变量
4.启动gdb attach 进程
[root@localhost ~]# gdb a.out 12826
GNU gdb (GDB) CentOS (7.0.1-45.el5.centos)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/a.out...done.
Attaching to program: /root/a.out, process 12826
Reading symbols from /lib/libpthread.so.0...(no debugging symbols found)...done.
[Thread debugging using libthread_db enabled]
[New Thread 0xb7524b90 (LWP 12828)]
[New Thread 0xb7f25b90 (LWP 12827)]
Loaded symbols for /lib/libpthread.so.0
Reading symbols from /lib/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2
0x00502402 in __kernel_vsyscall ()
(gdb) info threads //显示所有线程信息
3 Thread 0xb7f25b90 (LWP 12827) 0x00502402 in __kernel_vsyscall ()
2 Thread 0xb7524b90 (LWP 12828) 0x00502402 in __kernel_vsyscall ()
* 1 Thread 0xb7f266c0 (LWP 12826) 0x00502402 in __kernel_vsyscall ()
(gdb) thread 2 //跳到第2个线程
[Switching to thread 2 (Thread 0xb7524b90 (LWP 12828))]#0 0x00502402 in __kernel_vsyscall ()
(gdb) bt //查看线程2的堆栈,可以发现该线程堵塞在lock.c第17行
#0 0x00502402 in __kernel_vsyscall ()
#1 0x0072e839 in __lll_lock_wait () from /lib/libpthread.so.0
#2 0x00729e9f in _L_lock_885 () from /lib/libpthread.so.0
#3 0x00729d66 in pthread_mutex_lock () from /lib/libpthread.so.0
#4 0x080485b4 in work_thread (arg=0x0) at lock.c:17
#5 0x00727912 in start_thread () from /lib/libpthread.so.0
#6 0x0066660e in clone () from /lib/libc.so.6
(gdb)
参考自 http://blog.csdn.net/openxmpp/article/details/8615000
另一篇:
介绍了core dump之后,来看看如何在多线程调试中使用core dump。
使用 kill 命令产生 core dump文件:
kill -11 pid
这不还是杀掉进程嘛?没错,但是你用信号11杀掉它,会让进程产生一个 Segmentation Fault,从而(如果你没禁用 core dump 的话),导致一个 core dump。随后你得到一个 core 文件,里面包含了死锁的时候,进程的内存镜像,也就包括了正在纠结缠绵,生离死别从而产生死锁的那两个,没准是几个,线程们的,栈。
现在知道该怎么办了吧?用 gdb 打开这个 core 文件,然后
thread apply all bt
gdb 会打出所有线程的栈,如果你发现有那么几个栈停在 pthread_wait 或者类似调用上,大致就可以得出结论:就是它们几个儿女情长,耽误了整个进程。
下面我来举一个简单的例子(为了代码尽量简单,使用了C++11的thread library)
#include <iostream> #include <thread> #include <mutex> #include <chrono> using namespace std; mutex m1,m2; void func_2() { m2.lock(); cout<< "about to dead_lock"<<endl; m1.lock(); } void func_1() { m1.lock(); chrono::milliseconds dura( 1000 );// delay to trigger dead_lock this_thread::sleep_for( dura ); m2.lock(); } int main() { thread t1(func_1); thread t2(func_2); t1.join(); t2.join(); return 0; }
编译代码
$> g++ -Wall -std=c++11 dead_lock_demo.cpp -o dead_lock_demo -g -pthread
运行程序,发现程序打印出“about to dead_lock” 就不动了,现在我们使用gdb来调试。注意gdb的版本要高于7.0,之前使用过gdb6.3调试多线程是不行的。
在这之前需要先产生core dump文件:
$> ps -aux | grep dead_lock_demo
找出 dead_lock_demo 线程号,然后:
$> kill -11 pid
此时会生成core dump 文件,在我的系统上名字就是 core
然后调试:
$> gdb dead_lock_demo core
$> thread apply all bt
下面来看一下实际的过程:
从上图可以看出两个线程都阻塞在wait上,而且还给出了在哪一行代码中,很容易就定位到产生死锁的位置。
参考:http://www.cnblogs.com/zhuyp1015/p/3618863.html