设为首页 加入收藏

TOP

如何在linux下检测内存泄漏(五)
2011-03-18 13:13:13 来源:IBM 作者: 【 】 浏览:2908
Tags:何在 linux 检测 内存 泄漏
elete A 返回 delete operator A

在这一过程中,有两个技术问题,一个是 mutex 的可重入问题,一个是嵌套删除时 对全局变量(DELETE_FILE,DELETE_LINE)现场保护的问题。

所谓 mutex 的可重入问题,是指在同一个线程上下文中,连续对同一个 mutex 调用了多次 lock,然后连续调用了多次 unlock。这就是说我们的应用方式要求互斥锁有如下特性:

1. 要求在同一个线程上下文中,能够多次持有同一个互斥体。并且只有在同一线程上下文中调用相同次数的 unlock 才能放弃对互斥体的占有。

2. 对于不同线程上下文持有互斥体的企图,同一时间只有一个线程能够持有互斥体,并且只有在其释放互斥体之后,其他线程才能持有该互斥体。

Pthread_mutex_t 互斥体不具有以上特性,即使在同一上下文中,第二次调用 pthread_mutex_lock 将会挂起。因此,我们必须实现出自己的互斥体。在这里我们使用 semaphore 的特性实现了一个符合上述特性描述的互斥体 CCommonMutex(源代码见附件)。

为了支持特性 2,在这个 CCommonMutex 类中,封装了一个 semaphore,并在构造函数中令其资源值为 1,初始值为1。当调用 CCommonMutex::lock 接口时,调用 sem_wait 得到 semaphore,使信号量的资源为 0 从而让其他调用 lock 接口的线程挂起。当调用接口 CCommonMutex::unlock 时,调用 sem_post 使信号量资源恢复为 1,让其他挂起的线程中的一个持有信号量。

同时为了支持特性 1,在这个 CCommonMutex 增加了对于当前线程 pid 的判断和当前线程访问计数。当线程第一次调用 lock 接口时,我们调用 sem_wait 的同时,记录当前的 Pid 到成员变量 m_pid,并置访问计数为 1,同一线程(m_pid == getpid())其后的多次调用将只进行计数而不挂起。当调用 unlock 接口时,如果计数不为 1,则只需递减访问计数,直到递减访问计数为 1 才进行清除 pid、调用 sem_post。(具体代码可见附件)

嵌套删除时对全局变量(DELETE_FILE,DELETE_LINE)现场保护的问题是指,上述步骤中在 A 的析构函数中调用 delete m_pB 时,对全局变量(DELETE_FILE,DELETE_LINE)文件名和行号的赋值将覆盖主程序中调用 delete pA 时对全局变量(DELETE_FILE,DELETE_LINE)的赋值,造成了在执行 operator delete A 时,delete pA 的信息全部丢失。

要想对这些全局信息进行现场保护,最好用的就是堆栈了,在这里我们使用了 STL 提供的 stack 容器。在 DEBUG_DELETE 宏定义中,对全局变量(DELETE_FILE,DELETE_LINE)赋值之前,我们先判断是否前面已经有人对他们赋过值了--观察行号变量是否等于 0,如果不为 0,则应该将已有的信息压栈(调用一个全局函数 BuildStack() 将当前的全局文件名和行号数据压入一个全局堆栈globalStack),而后再对全局变量(DELETE_FILE,DELETE_LINE)赋值,再调用 delete operator。而在内存子系统的全局对象(appMemory)提供的 erase 接口里面,如果判断传入的文件名和行号为 0,则说明我们所需要的数据有可能被嵌套删除覆盖了,所以需要从堆栈中弹出相应的数据进行处理。

现在嵌套删除中的问题基本解决了,但是当嵌套删除与 "错误方式删除带来的问题"一节的最后所描述的第一和第三种异常情况同时出现的时候,由于用户的 delete 调用没有通过我们定义的 DEBUG_DELETE 宏,上述机制可能出现问题。其根本原因是我们利用stack 保留了经由我们的 DEBUG_DELETE 宏记录的 delete 信息的现场,以便在 operator delete 和全局对象(appMemory)的 erase 接口中使用,但是用户的没经过 DEBUG_DELETE 宏的 delete 操作却未曾进行压栈操作而直接调用了 operator delete,有可能将不属于这次操作的 delete 信息弹出,破坏了堆栈信息的顺序和有效性。那么,当我们因为无法找到这次及其后续的 delete 操作所对应的内存分配信息的时候,可能会打印出错误的 warning 信息。


展望

以上就是我们所实现的内存泄漏检测子系统的原理和技术方案,第一版的源代码在附件中,已经经过了较严格的系统测试。但是限于我们的 C++(www.cppentry.com) 知识水平和编程(www.cppentry.com)功底,在实现过程中肯定还有没有注意到的地方甚至是缺陷,希望能够得到大家的指正,我的 email 是hcode@21cn.com

在我们所实现的内存检测子系统基础上,可以继续搭建内存分配优化子系统,从而形成一个完整的内存子系统。一种内存分配优化子系统的实现方案是一次性分配大块的内存,并使用特定的数据结构管理之,当内存分配请求到来时,使用特定算法从这块大内存中划定所需的一块给用户使用,而用户使用完毕,在将其划为空闲内存。这种内存优化方式将内存分配释放转换为简单的数据处理,极大的减少了内存申请和释放所耗费的时间。


参考资料

1. 《More effective C++(www.cppentry.com)》Scott Meyers,候捷译

2. 《Effective c++》Scott Meyers,候捷译

3. 《深度探索C++(www.cppentry.com)对象模型》Stanley B.Lippman,候捷译

4. 《Unix环境高级编程(www.cppentry.com)》W.Richard Stevens,尤晋元等译

5. 源代码:检测子系统、动态内存监测、自定义的 mutex 类的源代码、简单的演示程序

关于作者

首页 上一页 2 3 4 5 下一页 尾页 5/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇C++断想 下一篇C 编程最佳实践