当程序的子模块数量和规模扩大以后,在开发阶段,系统长时间容许后常常会碰到下面一些bug:程序员
调试这些问题,固然能够从代码流程和逻辑出发,结合ps/gdb/proc/core等命令和信息,一步步挖出root cause。但通常要求对代码、线程关系和相关命令比较熟悉,通常耗时较长。因此,通常大型公司都封装了标准的glibc,作了一个wraaper,而后再wrapper里面加入了对上面调试的支持。还有的可采用专业内存泄漏等检查工具,去作代码检测。那么,对于咱工程师而言,可否能本身设计并实现一个资源检查工具呢?数据结构
针对上面的三个例子使用中的资源,咱们能够概括成两类:数量有限的共享资源,好比上面空间有限的内存和数量有限的文件句柄;须要独占的互斥资源,好比上面例子中提到的锁。 下面就分别针对这两种状况,分别展开分析。app
共享资源的特色是:总量有限,经过申请接口得到,使用完了以后经过释放接口归还。为了保证不浪费资源,这就要求程序在使用完了申请获得的资源以后,必须及时释放。而共享资源出现问题的状况,大部分是因为程序员遗忘没有释放形成的。所以,须要一种内部机制可以记录哪些资源使用了尚未释放,能够经过下面的步骤实现:ide
独享资源的特色是:互斥使用,基本上是先到先得,经过标志设置是否以备占有。为了保证不死锁,这就要求:程序在申请某个互斥资源的时候,须要检查它已经拥有的资源,是否被它正在申请的互斥资源的拥有者申请。
若是是,会死锁;不然,不会死锁。一样也能够经过下面的步骤实现:函数
根据上面原理的分析,咱们不难结合以前讲过的xlink、程序堆栈等技巧,选用合适的数据结构来实现。工具
根据2.1中的分析,须要先构造一张表来记录这些资源的地址,这张表要求插入方便,删去也迅速。为此,咱们能够用基于平衡二叉树、优先级队列或者hash的方法去实现这个表。对这个表的操做包括PQInsert()/PQRemove()/PQEmpty()等。 此后,就能够开始参考下面列出的针对共享资源泄漏检查的步骤去实现了。线程
能够基于标准的open/close/malloc/free等直接申请、是否公共资源的函数,去实现wrapper。
下面以open()、close()为例,伪码示例以下:
int wrapper_open(char * dev);
int wrapper_free(int fd);设计
仍是下面以open()、close()为例:调试
#define FILE ('f'<<24|'i<<16|'l'<<8|'e') int wrapper_open(char * dev) { int fd = real_open(dev); PQInsert(FILE, fd); return fd; } int wrapper_close(int fd) { int ret = 0; PQRemove(FILE, fd); ret = real_close(fd); return ret; }
有两种方式可使用支持资源泄漏检查的wrapper函数,一种是代码中之间调用open/close等函数对应的wrapper函数,另一种是借助gcc Xlink 的支持让标准的open/close函数“重定向”到wrapper_open/wrapper_close函数。显然,后面一种方法工做量最小、最优雅。具体的实现,能够参考前面关于Xlink的博文,下面列出了主要的几个步骤:code
gcc编译的flag中加入Xlinker改动
Xlinker --wrap=open -Xlinker --undefined=wrapper_open Xlinker --wrap=close -Xlinker --undefined=wrapper_close
新的头文件中加入声明
typeof(open) wrapper_open; typeof(close) wrapper_close; typeof(open) real_open; typeof(close) real_close;
一般,在程序快要结束退出的时候,会释放资源,末了能够经过共享资源泄漏检查函数去检查是否正的有资源泄漏。这个函数的主要实现的示例以下:
int FileRes_check(void) { if PQEmpty(FILE) { return PASS; } esle { PQDump(FILE); return FAIL; } }
根据上一篇博文中,关于如何获得程序堆栈中介绍的方法,咱们能够在申请共享资源的时候,具体来讲就是调用PQInsert(FILE, fd)时,取得当前程序的stack, 把堆栈信息和fd一块儿做为一项纪录插入到基于优先级队列、散列或者平衡二叉树实现的表中去。 一样,PQDump()除了打印没有关闭的文件句柄以外,还输出这个句柄对应打开时候的程序堆栈,根据这个堆栈信息,程序员可以定位到打开了这个没被关闭的句柄的代码的位置。而一般,这个句柄的关闭应该在它以后附近。
对独享资源死锁的检查的具体实现依赖的技术同上,差异就在死锁检查的逻辑和流程。读者能够自行尝试,等有时间了我也能够再来完善。
经过上面的这么多介绍能够看到,基于对共享资源和互斥资源使用特色的分析,咱们可以提出一种针对共享资源泄漏和独享资源死锁检查的通用原理。借助于合适的数据结构(二叉树、优先级队列或散列),基于 Xlinker方法、堆栈获取的API,咱们可以实现一种轻巧的、几乎不用改动已有代码的对开发者很是友好的资源检查功能。