Linux多线程调试之 利器Valgrind之helgrind

Valgrind的多线程调试工具  

Helgrind是Valgrind的一个重点功能 本节主要针对与多线程基本安全问题进行检测:【所有的代码环境都是在POSIX_THREAD模式下】



写线程代码时 经常碰到如下问题
1)  资源不安全访问 【就是多个线程在没有同步的情况下写某个资源体】

2) 死锁问题 

3)  POSIX pthreads API的错误使用 

4)  在前面几个基础上都能安全无误的情况下 多于多线程程序就是要能够能好将同步块尽量缩到最小  【这是一个很大的课题】


  解决问题:
    ​问题1:   调用Helgrind能够很好的解决掉  已基本例子为例:
    ​

  1. #include <pthread.h>


  2. int var = 0;


  3. void* child_fn ( void* arg ) {

  4.          var++;

  5.          return NULL;

  6. }


  7. int main ( void ) {

  8.          pthread_t child;

  9.          pthread_t child2;

  10.          pthread_create(&child,NULL, child_fn, NULL);

  11.          pthread_create(&child2,NULL,child_fn,NULL);

  12.          pthread_join(child,NULL);

  13.          pthread_join(child2,NULL);

  14.          return 0;

  15. }

明显var是共享的 不安全访问,调用Helgrind看看怎么能够检测出来
gcc -g thread_helgrind.c  -o thread_helgrind -lpthread
valgrind --tool=helgrind ./thread_helgrind 
可以看出valgrind弹出如下输出信息:

==25516== Helgrind, a thread error detector

==25516== Copyright (C) 2007-2013, and GNU GPL'd, by OpenWorks LLP et al.

==25516== Using Valgrind-3.9.0 and LibVEX; rerun with -h for copyright info

==25516== Command: ./thread_helgrind

==25516== 

==25516== ---Thread-Announcement------------------------------------------

==25516== 

==25516== Thread #3 was created

==25516==    at 0x415B3C8: clone (in /lib/i386-linux-gnu/libc-2.17.so)

==25516== 

==25516== ---Thread-Announcement------------------------------------------

==25516== 

==25516== Thread #2 was created

==25516==    at 0x415B3C8: clone (in /lib/i386-linux-gnu/libc-2.17.so)

==25516== 

==25516== ----------------------------------------------------------------

==25516== 

==25516== Possible data race during read of size 4 at 0x804A028 by thread #3

==25516== Locks held: none

==25516==    at 0x804851F: child_fn (thread_helgrind.c:12)

==25516==    by 0x402E5F6: mythread_wrapper (hg_intercepts.c:233)

==25516==    by 0x4054D77: start_thread (in /lib/i386-linux-gnu/libpthread-2.17.so)

==25516==    by 0x415B3DD: clone (in /lib/i386-linux-gnu/libc-2.17.so)

==25516== 

==25516== This conflicts with a previous write of size 4 by thread #2

==25516== Locks held: none

==25516==    at 0x8048527: child_fn (thread_helgrind.c:12)

==25516==    by 0x402E5F6: mythread_wrapper (hg_intercepts.c:233)

==25516==    by 0x4054D77: start_thread (in /lib/i386-linux-gnu/libpthread-2.17.so)

==25516==    by 0x415B3DD: clone (in /lib/i386-linux-gnu/libc-2.17.so)

==25516== 

==25516== ----------------------------------------------------------------

==25516== 

==25516== Possible data race during write of size 4 at 0x804A028 by thread #3

==25516== Locks held: none

==25516==    at 0x8048527: child_fn (thread_helgrind.c:12)

==25516==    by 0x402E5F6: mythread_wrapper (hg_intercepts.c:233)

==25516==    by 0x4054D77: start_thread (in /lib/i386-linux-gnu/libpthread-2.17.so)

==25516==    by 0x415B3DD: clone (in /lib/i386-linux-gnu/libc-2.17.so)

==25516== 

==25516== This conflicts with a previous write of size 4 by thread #2

==25516== Locks held: none

==25516==    at 0x8048527: child_fn (thread_helgrind.c:12)

==25516==    by 0x402E5F6: mythread_wrapper (hg_intercepts.c:233)

==25516==    by 0x4054D77: start_thread (in /lib/i386-linux-gnu/libpthread-2.17.so)

==25516==    by 0x415B3DD: clone (in /lib/i386-linux-gnu/libc-2.17.so)

==25516== 

==25516== 

==25516== For counts of detected and suppressed errors, rerun with: -v

==25516== Use --history-level=approx or =none to gain increased speed, at

==25516== the cost of reduced accuracy of conflicting-access information

==25516== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

可以看出绿色就显示的data_race错误  可以直接定位到var前后没有locks/



问题2:
    ​死锁问题是尽量避免  对于helgrind可以检测出加锁解锁顺序出现问题导致的死锁问题    这个问题我们可以好好看下:

    ​首先 看下一个正常的程序
    ​

#include <pthread.h>

 pthread_mutex_t mut_thread;

int var = 0;

void* child_fn ( void* arg ) {

pthread_mutex_lock(&mut_thread);

var++;

pthread_mutex_unlock(&mut_thread);

         return NULL;

}


int main ( void ) {

         pthread_t child;

         pthread_t child2;

         pthread_mutex_init(&mut_thread,NULL);

         pthread_create(&child,NULL, child_fn, NULL);

         pthread_create(&child2,NULL,child_fn,NULL);

         pthread_join(child,NULL);

         pthread_join(child2,NULL);

         return 0;

}

正常加锁解锁  没有问题  
在看下连续加2次锁的情况:

#include <pthread.h>

 pthread_mutex_t mut_thread;

int var = 0;

void* child_fn ( void* arg ) {

pthread_mutex_lock(&mut_thread);

var++;

pthread_mutex_lock(&mut_thread);

         return NULL;

}


int main ( void ) {

         pthread_t child;

         pthread_t child2;

         pthread_mutex_init(&mut_thread,NULL);

         pthread_create(&child,NULL, child_fn, NULL);

         pthread_create(&child2,NULL,child_fn,NULL);

         pthread_join(child,NULL);

         pthread_join(child2,NULL);

         return 0;

}


    看下这个helgrind打印出来的东西 【当然要杀死 不然会一直卡在那里动都不动一下】


==26534== Helgrind, a thread error detector

==26534== Copyright (C) 2007-2013, and GNU GPL'd, by OpenWorks LLP et al.

==26534== Using Valgrind-3.9.0 and LibVEX; rerun with -h for copyright info

==26534== Command: ./deadlock_helgrind

==26534== 

==26534== ---Thread-Announcement------------------------------------------

==26534== 

==26534== Thread #2 was created

==26534==    at 0x415B3C8: clone (in /lib/i386-linux-gnu/libc-2.17.so)

==26534== 

==26534== ----------------------------------------------------------------

==26534== 

==26534== Thread #2: Attempt to re-lock a non-recursive lock I already hold

==26534==    at 0x402E8E5: pthread_mutex_lock (hg_intercepts.c:507)

==26534==    by 0x80485C6: child_fn (deadlock_helgrind.c:14)

==26534==    by 0x402E5F6: mythread_wrapper (hg_intercepts.c:233)

==26534==    by 0x4054D77: start_thread (in /lib/i386-linux-gnu/libpthread-2.17.so)

==26534==    by 0x415B3DD: clone (in /lib/i386-linux-gnu/libc-2.17.so)

==26534==  Lock was previously acquired

==26534==    at 0x402E95D: pthread_mutex_lock (hg_intercepts.c:518)

==26534==    by 0x80485AD: child_fn (deadlock_helgrind.c:12)

==26534==    by 0x402E5F6: mythread_wrapper (hg_intercepts.c:233)

==26534==    by 0x4054D77: start_thread (in /lib/i386-linux-gnu/libpthread-2.17.so)

==26534==    by 0x415B3DD: clone (in /lib/i386-linux-gnu/libc-2.17.so)

==26534== 

^C==26534== ----------------------------------------------------------------

==26534== 

==26534== Thread #2: Exiting thread still holds 1 lock

==26534==    at 0x4001182: ??? (in /lib/i386-linux-gnu/ld-2.17.so)

==26534==    by 0x405B4D1: __lll_lock_wait (in /lib/i386-linux-gnu/libpthread-2.17.so)

==26534==    by 0x4056ED3: _L_lock_776 (in /lib/i386-linux-gnu/libpthread-2.17.so)

==26534==    by 0x4056D11: pthread_mutex_lock (in /lib/i386-linux-gnu/libpthread-2.17.so)

==26534==    by 0x402E914: pthread_mutex_lock (hg_intercepts.c:510)

==26534==    by 0x80485C6: child_fn (deadlock_helgrind.c:14)

==26534==    by 0x402E5F6: mythread_wrapper (hg_intercepts.c:233)

==26534==    by 0x4054D77: start_thread (in /lib/i386-linux-gnu/libpthread-2.17.so)

==26534==    by 0x415B3DD: clone (in /lib/i386-linux-gnu/libc-2.17.so)

==26534== 

==26534== 

==26534== For counts of detected and suppressed errors, rerun with: -v

==26534== Use --history-level=approx or =none to gain increased speed, at

==26534== the cost of reduced accuracy of conflicting-access information

==26534== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0

 Lock was previously acquired   对于自身线程来讲  这样的做法明显出问题 它自身还在尝试获取这个lock 这样就导致死锁的发生。可以定位到==26534==    by 0x80485C6: child_fn (deadlock_helgrind.c:14)出问题了 所以这个helgrind解决这类问题时还是非常的厉害的~!

接下来看一个2 mutex导致的问题:

#include <pthread.h>

 pthread_mutex_t mut_thread;

 pthread_mutex_t mut_thread1;

int var = 0;

void* child_fn ( void* arg ) {

pthread_mutex_lock(&mut_thread);

pthread_mutex_lock(&mut_thread1);

var++;

pthread_mutex_unlock(&mut_thread);

pthread_mutex_unlock(&mut_thread1);

         return NULL;

}

void* child_fn1(void *arg)

{

pthread_mutex_lock(&mut_thread1);

pthread_mutex_lock(&mut_thread);

var++;

pthread_mutex_unlock(&mut_thread1);

pthread_mutex_unlock(&mut_thread);

         return NULL;

}


int main ( void ) {

         pthread_t child;

         pthread_t child2;

         pthread_mutex_init(&mut_thread,NULL);

         pthread_mutex_init(&mut_thread1,NULL);

         pthread_create(&child,NULL, child_fn, NULL);

         pthread_create(&child2,NULL,child_fn1,NULL);

         pthread_join(child,NULL);

         pthread_join(child2,NULL);

         return 0;

}


加锁顺序导致死锁问题

==26785== Helgrind, a thread error detector

==26785== Copyright (C) 2007-2013, and GNU GPL'd, by OpenWorks LLP et al.

==26785== Using Valgrind-3.9.0 and LibVEX; rerun with -h for copyright info

==26785== Command: ./deadlock_helgrind

==26785== 

==26785== ---Thread-Announcement------------------------------------------

==26785== 

==26785== Thread #3 was created

==26785==    at 0x415B3C8: clone (in /lib/i386-linux-gnu/libc-2.17.so)

==26785== 

==26785== ----------------------------------------------------------------

==26785== 

==26785== Thread #3: lock order "0x804A038 before 0x804A050" violated

==26785== 

==26785== Observed (incorrect) order is: acquisition of lock at 0x804A050

==26785==    at 0x402E95D: pthread_mutex_lock (hg_intercepts.c:518)

==26785==    by 0x8048637: child_fn1 (deadlock_helgrind.c:22)

==26785==    by 0x402E5F6: mythread_wrapper (hg_intercepts.c:233)

==26785==    by 0x4054D77: start_thread (in /lib/i386-linux-gnu/libpthread-2.17.so)

==26785==    by 0x415B3DD: clone (in /lib/i386-linux-gnu/libc-2.17.so)

==26785== 

==26785==  followed by a later acquisition of lock at 0x804A038

==26785==    at 0x402E95D: pthread_mutex_lock (hg_intercepts.c:518)

==26785==    by 0x8048643: child_fn1 (deadlock_helgrind.c:23)

==26785==    by 0x402E5F6: mythread_wrapper (hg_intercepts.c:233)

==26785==    by 0x4054D77: start_thread (in /lib/i386-linux-gnu/libpthread-2.17.so)

==26785==    by 0x415B3DD: clone (in /lib/i386-linux-gnu/libc-2.17.so)

==26785== 

==26785== Required order was established by acquisition of lock at 0x804A038

==26785==    at 0x402E95D: pthread_mutex_lock (hg_intercepts.c:518)

==26785==    by 0x80485ED: child_fn (deadlock_helgrind.c:13)

==26785==    by 0x402E5F6: mythread_wrapper (hg_intercepts.c:233)

==26785==    by 0x4054D77: start_thread (in /lib/i386-linux-gnu/libpthread-2.17.so)

==26785==    by 0x415B3DD: clone (in /lib/i386-linux-gnu/libc-2.17.so)

==26785== 

==26785==  followed by a later acquisition of lock at 0x804A050

==26785==    at 0x402E95D: pthread_mutex_lock (hg_intercepts.c:518)

==26785==    by 0x80485F9: child_fn (deadlock_helgrind.c:14)

==26785==    by 0x402E5F6: mythread_wrapper (hg_intercepts.c:233)

==26785==    by 0x4054D77: start_thread (in /lib/i386-linux-gnu/libpthread-2.17.so)

==26785==    by 0x415B3DD: clone (in /lib/i386-linux-gnu/libc-2.17.so)

==26785== 

==26785== 

==26785== For counts of detected and suppressed errors, rerun with: -v

==26785== Use --history-level=approx or =none to gain increased speed, at

==26785== the cost of reduced accuracy of conflicting-access information

==26785== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 9 from 9



这是观察法得出的  加锁顺序出错导致了这种情况的发生。  

posix_thread erro这里就不列举了  这个完全是看基础。

下篇待续

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux 多线程编程是一种技术,可以让一个程序并行地处理多个任务。它可以提高程序的执行效率,更有效地使用计算机的资源,并减少系统响应时间。Linux 是一种流行的操作系统,支持多线程编程。多线程编程是指在同一进程中同时执行多个线程,每个线程执行自己的任务。下面是一些关于 Linux 多线程编程的基本概念和技术: 1. 线程和进程的区别:线程是进程内的一个执行单元,进程是操作系统分配资源的一个独立单位。 2. 线程同步:线程同步是指协调多个线程之间的执行顺序,防止出现数据竞争和死锁等问题。常见的线程同步技术包括互斥量、条件变量和信号量等。 3. 线程池:线程池是一种预先创建一组线程并重复使用的技术。线程池可以提高多线程程序的效率和性能。 4. POSIX 线程库:POSIX 线程库是 Linux 操作系统支持的一种多线程编程接口,提供了一套标准的多线程 API,包括创建、销毁、同步和调度线程等功能。 5. 多线程调试多线程程序的调试需要注意避免数据竞争和死锁等问题,可以使用调试工具和技术,如 gdb 和 Valgrind 等。 总之,多线程编程是 Linux 程序员必备的技能之一,掌握多线程编程技术可以提高程序的效率和性能,同时也需要注意避免常见的线程问题。 ### 回答2: Linux 多线程编程是一种在 Linux 操作系统上开发并行应用程序的方式,它允许一个程序同时执行多个线程,从而提高程序的响应速度和运行效率。在 Linux 中,线程是轻量级的进程,它们共享同一进程的资源和数据,可以同时运行在不同的 CPU 核心上,使得程序在多核系统中具有更好的性能表现。 Linux 提供了多种多线程编程的 API,其中最常用的是 pthreads 库,它是一组 C 语言函数,可用于创建、同步和管理多个线程。使用 pthreads 库编写多线程程序的基本步骤包括定义线程函数、创建线程、执行线程、同步线程和销毁线程,这些步骤需要程序员显式地调用相关的 API 函数来实现。 在编写多线程程序时,必须考虑线程之间的共享资源和同步问题。共享资源包括程序的数据、文件、网络连接等,可以使用临界区、互斥锁、信号量等技术来保护。同步问题则是确保多个线程之间按照正确的顺序执行,不会产生死锁、饥饿等问题,可以使用信号量、互斥锁、条件变量等技术来实现。 此外,多线程编程还需要考虑到线程的调度问题,即如何让不同的线程在不同的时间片内执行,从而实现线程的抢占和优先级控制。Linux 提供了优先级调度器和时间片分配器来实现线程的调度,程序员可以根据需要设定优先级和时间片长度来控制线程的执行顺序和时间切片。 总之,Linux 多线程编程是一种高效、灵活和可扩展的编程模型,能够充分利用多核系统的性能,并实现程序的并行化和异步化。开发者需要熟悉多线程编程的基本概念、API 和技术,遵循正确的资源共享和同步策略,才能编写出高质量、可靠、并发的多线程程序。 ### 回答3: Linux 多线程编程是指在 Linux 操作系统下使用多个线程同时运行程序来提高程序的运行效率、并实现多任务处理。多线程编程具有使用方便、管理灵活、响应速度相对于进程更快的优势,常被应用于高并发服务器中。 在 Linux 中,多线程编程的实现主要依赖于 pthread 库。该库提供了一些线程函数和数据结构,用于创建、控制、同步和分离线程等操作。以下是一些常用的 pthread 函数: 1. pthread_create():用于创建一个新线程; 2. pthread_join():主线程阻塞等待子线程结束; 3. pthread_exit():用于在线程代码中退出线程; 4. pthread_mutex_lock():线程加锁; 5. pthread_mutex_unlock():线程解锁; 6. pthread_cond_wait():线程等待; 7. pthread_cond_signal():唤醒等待的线程。 除了 pthread 库,Linux 还提供了其它一些可用于多线程编程的工具和技术,如: 1. OpenMP:一种基于共享内存的多线程编程模型; 2. MPI:一种消息传递的分布式并行编程模型。 当进行多线程编程时,需要注意以下几个方面: 1. 多线程编程的并发性可能会导致一些竞态条件,需要使用锁或互斥量进行同步; 2. 在实现多线程编程过程中,应该尽量避免使用全局变量或静态变量; 3. 在多线程编程实践中,应注意资源的释放和内存泄漏的问题。 总之,Linux 多线程编程是一项非常重要的技能,它能够在提高程序性能和响应速度方面发挥重要作用。熟练掌握多线程编程技术,对于编写高效稳定的服务端程序和优化多线程应用程序的性能都非常有益。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值