当前位置:Linux教程 - Linux - Solaris2.4 多线程编程指南6--编译和调试

Solaris2.4 多线程编程指南6--编译和调试

本文出自:BBS水木清华站 作者:Mccartney (coolcat) (2002-01-29 20:32:52)

6 编译和调试

本章描述了怎样编译和调试多线程程序。
编译一个多线程应用程序
调试一个多线程应用程序

6.1编译一个多线程应用程序

6.1.1使用C编译器

确认你拥有如下软件,否则将无法正常编译和连接多线程程序
· 头文件:thread.h errno.h
· 标准C编译器
· 标准Solaris连接器
· 线程库(libthread)
· MT-Safe库(libc, libm, libw, libintl, libmalloc,
libmapmalloc, libnsl, 等等)

6.1.2用替代(_REENTRANT)标志进行编译

在编译多线程程序时使用""-D _REENTRANT""标志。
这个标志必须在编译应用程序的每一个模块时都使用。如果没有这个标志,将使用errno, stdio等等的旧的定义。如果要编译一个单线程应用程序,不要使用这个标志。

*新旧连接需要小心

表6-1显示了多线程目标代码模块与旧的代码模块连接时需要非常慎重。
表6-1 在编译多线程程序时使用""-D _REENTRANT""标志
文件类型 编译 参考 返回
旧的目标文件(非线程版)和新的目标文件 没有 ""-D _REENTRANT"" 标志。 静态储存 传统的errno
新的目标文件 有 ""-D _REENTRANT""标志。 __errno,新的二进制入口 线程定义errno的地址
用libnsl 里的TLI编程 有 ""-D _REENTRANT""标志(必须)。 __t_errno,一个新的入口 线程定义t_errno的地址

6.1.3使用libthread

为了在连接时使用libthread,需要在ld命令行里,-lc参数之前,指定 -lthread,或者在cc 命令行的最后指定。
如果应用程序没有连接libthread,则对该库中的函数调用不产生实际操作。
Libc定义libthread为空过程。???真正的过程是在应用程序既连接libc也 连接libthread时由libthread加入的。
如果一个ld命令行包含了以下的字段:.o''s ... -lc -lthread ...,则C函数库的行为没有被定义。???
不要在单线程程序中使用-lthread。这样做将在连接时建立多线程机制,在运 行时将被初始化。这样做不但浪费资源,而且在调试中会对运行结果有不正确的显示。

6.1.4使用非C的编译器

线程库使用libc中的如下内容:
· 系统调用包装器(system call wrappers)
· 用来显示出错信息的调用(通常是printf)
· 运行时的连接支持来解析符号(因为库是动态连接的)

你也可以写自己的系统调用包装器和自己的printf函数,并且在连接时(而不 是在运行时)进行符号解析,这样可以消除对libc的依赖。
如果线程使用应用程序提供的堆栈,则线程库不使用动态分配内存的办法。 Thr_create(3T)函数可以由应用程序指定自己的堆栈。

6.2调试多线程应用程序

6.2.1一般的疏漏
以下列出可以导致多线程出错的常见疏漏:
· 给新线程传递参数时使用局部或全局变量
· 在没有同步机制的保护下访问全局内存
· 两个线程以不同的顺序去申请两个资源导致死锁(两个线程各自占有一个资源并相执不下)
· 在同步保护中有隐藏的漏洞。例如可能有如下情况:一个有同步机制(例如互斥 锁)保护的代码段包含一个先释放再重新获得同步机制的函数调用,结果是全局内存 实际上没有得到保护。
· 有隐匿的,重复或递归的大自动数组的使用可能导致问题,因为多线程程序的堆 栈容量比单线程程序有更多的限制。
· 指定的堆栈空间不够。
· 没有通过线程库的调用指定堆栈。
注意,多线程程序(特别是有错误的)经常在相同输入的情况下得到不同的结 果,因为线程调度的顺序不同。
一般的,多线程bug具有统计性,而不是确定性。在调试时,跟踪的办法将会比 设断点的办法好些。

6.2.2使用adb

如果你在一个多线程程序当中绑定所有线程,一个线程和一个LWP是同步的。然 后你通过如下支持多线程编程的adb命令访问每一个线程。

表6-2 MT adb命令
-------------------------------------
pid:A 绑定在进程pid上,这将停止进程及其所有LWP
∶R 与进程分离,这将恢复进程及其所有LWP
$L 显示在(停止的)进程中所有的活动的LWP
n:l 将焦点切换到第n号LWP
$l 显示当前焦点所在的LWP
num:i 忽略信号码为num的信号

6.2.3使用dbx

使用dbx,可以调试和执行用C++, ANSI C, FORTRAN和PASCAL的源程序。Dbx使用与SPARCworks? Debugger相同的命令,但使用标准终端(tty)接口。Dbx和
SPARCworks Debugger现在都支持多线程程序。
要得到dbx和Debugger的全面认识,请参考SunPro dbx(1) man page和
《Debugging a Program》用户指南。
以下的dbx选项支持多线程。
表6-3 给MT程序使用的dbx选项
Cont at line[sig signo id] 在信号signo发生时继续执行第line行。
参见dbx的命令语言的循环控制里的continue。
如果有id参数,则指定继续哪一个线程或LWP。
缺省设置为all。
Lwp 显示当前LWP。切换到给定LWP[lwpid]
Lwps 列出当前进程的所有LWP
Next … tid 单步执行指定线程。如果一个函数调用被跳过,
所有的LWP在该函数调用期间重新开始???非
活动线程不能被单步执行
Next … lid 单步执行指定LWP。在跳过函数时并不隐含地恢
复所有的LWP。在该LWP上的线程是活动的。
Step … tid 单步执行指定线程。如果一个函数调用被跳过,
所有的LWP在该函数调用期间重新开始???非
活动线程不能被单步执行
Step … lid 单步执行指定LWP。在跳过函数时并不隐含地恢
复所有的LWP。
Stepi … lid 指定的LWP
Stepi … tid 在LWP上的线程是活动的。
Thread 显示当前线程。切换到线程tid。在以下情况中,
一个可选的tid指当前线程。
Thread -info[tid] 打印指定线程的所有已知情况。
Thread -locks[tid] 打印被指定线程控制的所有锁
Thread -suspend[tid] 把指定线程置于挂起状态。
Thread -continue[tid] 使指定线程退出挂起状态。
Thread -hide[tid] 隐藏指定(或当前)线程,在普通线程列表中
将不被显示出来
Thread -unhide [tid] 解除指定线程的隐藏状态
Allthread-unhide 解除所有线程的隐藏状态
Threads 打印已知线程的列表
Threads-all 打印所有线程(包括通常不被打印的,zombies)
All|filterthreads-mode 控制threads命令打印所有线程还是有选择地列表
Auto|manualthreads-mode 使在GUI界面里线程监控器(Thread Inspector)
线程列表得以自动更新
Threads -mode 显示当前模式。Any of the previous forms
can be followed by a
thread or LWP ID to get the traceback for the specified entity.