Linux GDB调试死锁问题

1. 死锁介绍

1.1 锁的简介

由于多线程的模式下,各个线程并发运行(注意“并发和“并行”的区别),为了保证各个线程对公共资源的访问时出现数据不一致性的问题,出现了锁的机制。

Linux系统编程中最常见的锁机制是通过互斥量(mutex)来实现的。任一时刻只有一个线程可以对互斥量mutex上锁(或说成持有该互斥量),在被持有期间,其它线程就无法对它进行上锁(这也是互斥量名字中“互斥”的由来),其它尝试加锁的线程都会休眠(注意休眠和挂起的区别)释放cpu资源,并被记录到此互斥量的等待队列中。持有的线程释放互斥量后,内核会根据调度机制从等待队列中选择一个线程使其持有互斥量,队列中的其它线程将会继续等待。

1.2 死锁的常见形式

网上关于死锁的资料有很多,比如死锁形成的四要素等等。我这里只按我的理解以白话的形式进行说明。死锁的两种常见形式为 "AA" 和 "ABAB"

(1)AA:重复上锁

发生在同一个线程中,加锁后未解锁就再次加锁,最常发生错误的情况就是程序异常退出循环前没有释放锁,导致再次上锁后发生错误,伪代码模型如下:

Linux GDB调试死锁问题_第1张图片

(2)ABBA:多个线程持有彼此在等待的锁

线程线程1持有锁A,线程2持有锁B,同时线程1等待锁B,线程2等待锁A,这种情况下就会一直死等下去。

2. GDB调试死锁的实例

2.1 写一个死锁程序

mutex.c,代码如下:

   #include
   #include
  
   pthread_mutex_t mutex_1 = PTHREAD_MUTEX_INITIALIZER;
   pthread_mutex_t mutex_2 = PTHREAD_MUTEX_INITIALIZER;
  
   void *pthread_test_1(void *arg)
   {
      pthread_mutex_lock(&mutex_1);
      sleep(1);   //休眠以保证pthread_test_2线程运行至持有mutex_2
      pthread_mutex_lock(&mutex_2);
      pthread_mutex_unlock(&mutex_1);
      pthread_mutex_unlock(&mutex_2);
  }
 
  void *pthread_test_2(void *arg)
  {
      pthread_mutex_lock(&mutex_2);
      sleep(1);    //休眠以保证pthread_test_1线程运行至持有mutex_1
      pthread_mutex_lock(&mutex_1);
      pthread_mutex_unlock(&mutex_2);
      pthread_mutex_unlock(&mutex_1);
  }
   
  int main(void)
  {
      pthread_t tid1, tid2;
      pthread_create(&tid1, NULL, pthread_test_1, NULL);
      pthread_create(&tid2, NULL, pthread_test_2, NULL);
 
      pthread_join(&tid1, NULL);
      pthread_join(&tid2, NULL);
      return 0;
  }

2.2 GDB调试过程

gcc 编译:gcc mutex.c -o ppt,我这里给可执行程序随便起了一个名字叫“ppt”

(1)运行死锁程序

事先运行可执行程序./ppt,然后再启动GDB进行调试(因为正常情况很可能就是这样:事先已经运行了程序,发现它可能产生死锁,所以我们用GDB对它进行调试)。

(2)pidof命令获取进程号

(3)启用GDB,然后attach [PID]调试已经在运行的进程

Linux GDB调试死锁问题_第2张图片

注:启用gdb 然后attach [PID]发现Operation not permitted,提示,我们可以try as root

(4)查看线程的堆栈信息

thread apply all bt:查看所有线程的堆栈信息

当发现有多个线程停留在 "__lll_lock_wait" 上时,很可能说明发生了死锁,或者其它问题比如持有锁的线程无法退出,导致其他线程都阻塞在了加锁上。

Linux GDB调试死锁问题_第3张图片

从上图可以看出pthread_test_2这个线程在等待给mutex_1上锁,pthread_test_1这个线程在等待给mutex_2上锁,还不能就此判断出发生了死锁。需要在进一步查看mutex_1和mutex_2的信息。

 (5)查看锁的信息

1)如果知道互斥量的名字,可以直接运行 "print mutex_name"命令来查看。

2)如果不知道互斥量的名字,可以通过 "获取互斥量地址处的数据方式" 来查看

print *(pthread_mutex *)(address):就是从address所标识的地址处取出pthread_mutex类型的数据。

Linux GDB调试死锁问题_第4张图片

 由上图可以看出,mutex_1被ID为9641的线程持有,而9641为pthread_test_1,mutex_2被ID为9642的线程持有,而9642为phtread_test_2。

综上:pthread_test_1持有mutex_1,等待mutex_2,pthread_test_2持有mutex_2,等待mutex_1,得出结论发生了死锁。

你可能感兴趣的:(Linux,GDB,linux,gdb,c语言)