gdb多線程調試

死鎖:一種情形,此時執行程序中兩個或多個線程發生永久堵塞(等待),每一個線程都在等待被  html

其餘線程佔用並堵塞了的資源。例如,若是線程A鎖住了記錄1並等待記錄2,而線程B鎖住了記錄2並等待記錄1,這樣兩個線程就發生了死鎖現象。
 
gdb調試死鎖的方法:
gdb 
attach pid
thread apply all bt
 
找到_lll_lock_wait 鎖等待的地方。
而後查找該鎖被哪一個線程鎖住了。
例如:
查看哪一個線程擁有互斥體
(gdb) print AccountA_mutex
$1 = {__m_reserved = 2, __m_count = 0, __m_owner = 0x2527,
__m_kind = 0, __m_lock
= {__status = 1, __spinlock = 0}}
(gdb) print 0x2527
$2 = 9511
(gdb) print AccountB_mutex
$3 = {__m_reserved = 2, __m_count = 0, __m_owner = 0x2529,
__m_kind = 0, __m_lock = {__status = 1, __spinlock = 0}}
(gdb) print 0x2529
$4 = 9513
(gdb)
從上面的命令中,咱們能夠看出AccontA_mutex是被線程 5(LWP 9511)加鎖(擁有)的,而AccontB_mutex是被線程 3(LWP 9513)加鎖(擁有)的。

 

找出死鎖的地方,對應檢查代碼就能夠了。死鎖大可能是對鎖的使用發生交叉所致的,解決死鎖的方法常有:

有序資源分配法

是操做系統中預防死鎖的一種算法,這種算法資源按某種規則系統中的全部資源統一編號(例如打印機爲一、磁帶機爲二、磁盤爲三、等等),申請時必須以上升的次序。
系統要求申請進程:  
一、對它所必須使用的並且屬於同一類的全部資源,必須一次申請完;  
二、在申請不一樣類資源時,必須按各種設備的編號依次申請。
例如:進程PA,使用資源的順序是R1,R2; 
           進程PB,使用資源的順序是R2,R1;
若採用動態分配有可能造成環路條件,形成死鎖。  
採用有序資源分配法:R1的編號爲1,R2的編號爲2;  
PA:申請次序應是:R1,R2  
PB:申請次序應是:R1,R2  
這樣就破壞了環路條件,避免了死鎖的發生。
另外,還有死鎖避免,死鎖檢測與恢復等。 

銀行家算法

咱們能夠把 操做系統看做是銀行家,操做系統管理的資源至關於銀行家管理的資金,進程向操做系統請求分配資源至關於用戶向銀行家貸款。
爲保證資金的安全,銀行家規定:
(1) 當一個顧客對資金的最大需求量不超過銀行家現有的資金時就可接納該顧客;
(2) 顧客能夠分期貸款,但貸款的總數不能超過最大需求量;
(3) 當銀行家現有的資金不能知足顧客尚需的貸款數額時,對顧客的貸款可推遲支付,但總能使顧客在有限的時間裏獲得貸款;
(4) 當顧客獲得所需的所有資金後,必定能在有限的時間裏歸還全部的資金.
操做系統按照銀行家制定的規則爲進程分配資源,當進程首次申請資源時,要測試該進程對資源的最大需求量,若是系統現存的資源能夠知足它的最大需求量則按當前的申請量分配資源,不然就推遲分配。當進程在執行中繼續申請資源時,先測試該進程本次申請的資源數是否超過了該資源所剩餘的總量。若超過則拒絕分配資源,若能知足則按當前的申請量分配資源,不然也要推遲分配。
 
參考:http://blog.csdn.net/wqlyqy/article/details/9195625
 
/**
  死鎖調試
  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 locklinux

(程序掛起)ios

 

3.查找進程idc++

[root@localhost ~]# ps -e | grep a.outredis

12826 pts/3    00:00:00 a.out //進程id爲12826算法

 gdb多線程調試命令:centos

(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。app

(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

相關文章
相關標籤/搜索