死锁分析

8 篇文章 1 订阅
文章转自:http://www.ibm.com/developerworks/cn/linux/l-cn-deadlock/

- 概述:

     死锁 (deallocks): 是指两个或两个以上的进程(线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程(线程)称为死锁进程(线程)。 由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程(线程)在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象死锁。

     一种交叉持锁死锁的情形,此时执行程序中两个或多个线程发生永久堵塞(等待),每个线程都在等待被其它线程占用并堵塞了的资源。例如,如果线程 1 锁住了记录 A 并等待记录 B,而线程 2 锁住了记录 B 并等待记录 A,这样两个线程就发生了死锁现象。在计算机系统中 , 如果系统的资源分配策略不当,更常见的可能是程序员写的程序有错误等,则会导致进程因竞争资源不当而产生死锁的现象。

- 死锁的四个必要条件

(1) 互斥条件:一个资源每次只能被一个进程(线程)使用。
(2) 请求与保持条件:一个进程(线程)因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件 : 此进程(线程)已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件 : 多个进程(线程)之间形成一种头尾相接的循环等待资源关系

- 死锁的分析方法
通过其产生的必要条件知道,等待资源时形成一种闭环,导致循环等待,那么就要找出这几个不同线程的等待环路。可以分析形成死锁后现象,死锁状态中的各个线程是阻塞不动无法推进的,也即是这些线程的调用堆栈是不会再发生变化了,不过也有可能非死锁线程的在一定时间内是不变的。所以我们可以比较线程不同时刻的堆栈信息,另外最好也要切换场景(切换线程)保证正常的线程在动,线程堆栈就会发生变化,这样就可以过滤出死锁的线程,对怀疑的死锁线程查看其对锁的使用情况。

- 死锁的分析示例
  实例代码lock.cpp
   
 #include <unistd.h> 
 #include <pthread.h> 
 #include <string.h> 
 #include <stdio.h>
 
 pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; 
 pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; 
 pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER; 
 pthread_mutex_t mutex4 = PTHREAD_MUTEX_INITIALIZER; 

 static int sequence1 = 0; 
 static int sequence2 = 0; 

 int func1() 
 { 
    pthread_mutex_lock(&mutex1); 
    ++sequence1; 
    sleep(1); 
	//printf("[threadID:%d,fun:%s,line:%d]\n",pthread_self(),__FUNCTION__,__LINE__);
    pthread_mutex_lock(&mutex2); 
    ++sequence2; 
	//printf("[threadID:%d,fun:%s,line:%d]\n",pthread_self(),__FUNCTION__,__LINE__);
    pthread_mutex_unlock(&mutex2); 
    pthread_mutex_unlock(&mutex1); 

    return sequence1; 
 } 

 int func2() 
 { 
    pthread_mutex_lock(&mutex2); 
    ++sequence2; 
    sleep(1); 
	//printf("[threadID:%d,fun:%s,line:%d]\n",pthread_self(),__FUNCTION__,__LINE__);
    pthread_mutex_lock(&mutex1); 
    ++sequence1; 
	//printf("[threadID:%d,fun:%s,line:%d]\n",pthread_self(),__FUNCTION__,__LINE__);
    pthread_mutex_unlock(&mutex1); 
    pthread_mutex_unlock(&mutex2); 

    return sequence2; 
 } 

 void* thread1(void* arg) 
 { 
    while (1) 
    { 
        int iRetValue = func1(); 

        if (iRetValue == 100000) 
        { 
            pthread_exit(NULL); 
        } 
    } 
 } 

 void* thread2(void* arg) 
 { 
    while (1) 
    { 
        int iRetValue = func2(); 

        if (iRetValue == 100000) 
        { 
            pthread_exit(NULL); 
        } 
    } 
 } 

 void* thread3(void* arg) 
 { 
    while (1) 
    { 
        sleep(1); 
		printf("[threadID:%d,fun:%s,line:%d]\n",pthread_self(),__FUNCTION__,__LINE__);
        char szBuf[128]; 
        memset(szBuf, 0, sizeof(szBuf)); 
        strcpy(szBuf, "thread3"); 
    } 
 } 

 void* thread4(void* arg) 
 { 
    while (1) 
    { 
        sleep(1); 
		printf("[threadID:%d,fun:%s,line:%d]\n",pthread_self(),__FUNCTION__,__LINE__);
        char szBuf[128]; 
        memset(szBuf, 0, sizeof(szBuf)); 
        strcpy(szBuf, "thread3"); 
    } 
 } 

 int main() 
 { 
    pthread_t tid[4]; 
	
    if (pthread_create(&tid[0], NULL, thread1, NULL) != 0) 
    { 
        _exit(1); 
    } 
	//printf("[threadID:%d,fun:%s,line:%d]\n",tid[0],__FUNCTION__,__LINE__);
	
    if (pthread_create(&tid[1], NULL, thread2, NULL) != 0) 
    { 
        _exit(1); 
    } 
	//printf("[threadID:%d,fun:%s,line:%d]\n",tid[1],__FUNCTION__,__LINE__);
    if (pthread_create(&tid[2], NULL, thread3, NULL) != 0) 
    { 
        _exit(1); 
    } 
	//printf("[threadID:%d,fun:%s,line:%d]\n",tid[2],__FUNCTION__,__LINE__);
    if (pthread_create(&tid[3], NULL, thread4, NULL) != 0) 
    { 
        _exit(1); 
    } 
	//printf("[threadID:%d,fun:%s,line:%d]\n",tid[3],__FUNCTION__,__LINE__);

    sleep(5); 
    //pthread_cancel(tid[0]); 

    pthread_join(tid[0], NULL); 
    pthread_join(tid[1], NULL); 
    pthread_join(tid[2], NULL); 
    pthread_join(tid[3], NULL); 

    pthread_mutex_destroy(&mutex1); 
    pthread_mutex_destroy(&mutex2); 
    pthread_mutex_destroy(&mutex3); 
    pthread_mutex_destroy(&mutex4); 

    return 0; 
 }
 

   编译,执行
   g++ -g lock.cpp -o lock -lpthread
   ./lock &
   
   查看进程号
   ps -elf |grep lock
   0 S root     28778 27536  0  80   0 - 10959 stext  17:49 pts/2    00:00:00 ./lock

   通过pstack查看线程堆栈
   
   重复多次,比较不同线程的堆栈信息,一直未发生变化的就可能是死锁线程。

   运行gdb,查看堆栈,锁的使用信息
   

   查看线程,分析锁信息
      
   info thread 查看所有线程,thread n 根据线程号切换线程,where查看当前线程堆栈信息,f n 查看堆栈第n层信息
   通过上图可以发现thread 2 和thread 3一个锁住了mutex2,一个锁住了mutex1,然后互相等待,导致死锁。

- pstack源码
  碰到系统不支持pstack的可以下载源码,编译安装。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值