如何为大型项目制作一个简单的工具来检测Linux中的双倍空闲或内存溢出

How to make a simple tool to detect double free or memory overflow in Linux for a large project?

本文关键字:Linux 检测 溢出 内存 工具 大型项目 简单 一个      更新时间:2023-10-16

我有一个运行Linux的大型嵌入式项目。此外,它还运行着各种进程和线程。我不能记录所有的malloc和新调用,因为这会使嵌入式机顶盒变得迟钝。此外,由于互斥超时或其他原因,迟缓可能会导致崩溃。因此,我想制作一个工具来帮助调试与内存相关的问题,比如内存溢出。

例如,当您执行一个4字节的malloc时。但是,你写的是8个字节。这可能会在所分配的其他数据块上产生问题。数据头的另一块可以被篡改。因此,free()将失败或崩溃。我如何制作一个工具来检测这样的问题。此外,还有一个追踪内存泄漏的工具。有办法做到这一点吗?我不能使用valgrind,因为它会减慢我的STB。因此,我想开发我的工具,可以检查内存头损坏或内存泄漏。根据我的选择,它可以进行内存损坏检查或内存泄漏检测。此外,它应该是一个轻量级。

首先,可能没有办法将其称为"简单"。其次,如果您使用C++,我强烈建议不要使用malloc/free,而是使用new/delete。覆盖这些运算符的选项要灵活得多。

C++提供了许多工具来提高内存安全性:

  1. 智能指针(性能成本确实值得安全改进)
  2. 将事物封装在类中。例如,如果您使用std::array::at(i),那么如果您的访问超出范围,它将抛出异常。参考
  3. 最后,在代码中正确使用断言可以在很大程度上捕捉错误

我的观点只是,您不应该依赖调试工具来否定使用良好C++编程方法的必要性。

好的,所以现在下一步你需要覆盖新的和删除。谷歌搜索将提供许多方法来做到这一点。链接1对于您的问题,全局重载delete/new可能更有意义。

缓冲区溢出检测这是问题的第一部分。您需要做的是在新的重载指令中分配额外的内存,以便在内存前后有一些内存缓冲区,然后只返回中间部分。缓冲区的大小由您选择。伪代码:

inline void* operator new(size_t s)
{
void* mem = malloc(s+2*BUFFER);
memset(mem,0x5A,s+2*BUFFER);
return (mem+BUFFER)
}

在未来的某个阶段,您需要检查BUFFER区域是否保持0x5A的值。你可能应该在对free()的调用中这样做,但你也可以有自己的函数来做这件事,你可以定期调用它。为了加快这个过程,也许可以使用memcmp这样的函数。

内存泄漏检测检测内存泄漏并非易事。首先,我建议尽可能使用基于堆栈的对象,以避免在不需要时在堆上分配内存。关于内存泄漏的主要问题是知道某个内存块是否应该被删除。99%的内存泄漏问题可能只需使用智能指针就可以解决。然而,最难捕捉的内存泄漏之一是不断增长的数据结构。(例如一个随时间缓慢增长的链表)

首先,在重载的新malloc函数中,保留当前分配的所有内存的列表。还有一个分配内存总数的计数器。

方法1:阈值检测:

基本上,每当程序的内存使用量超过阈值时,都会报告并增加阈值。如果您的程序在继续运行的过程中继续超过阈值,则会出现问题。

方法2:比较分析:在pseudeo代码中:

Value1 = currentAmountOfMemoryUsed;
runSomeCode()
if (currentAmountOfMemoryUsed != Value1) reportProblem()

这是否可能在很大程度上取决于runSomeCode()中发生的事情,因为有些代码可以合法地"保存"一些内存,以便以后再次运行。

方法3:程序退出时的泄漏检测:

前提是,如果你的代码写得100%正确,那么在你的程序存在时,分配的每一位内存都应该被释放。这种方法并不总是可行的,因为也许你的程序需要无限期运行,而且你的程序可能会因为你的错误而在检测到之前很久就出现故障。

编译器支持在较低级别上,大多数编译器都支持进入整个内存管理系统,但处理此问题的方法100%特定于编译器/平台。例如Visual Studio C++这就是为什么我强烈建议不要直接使用malloc/free,因为这对以这种方式进行调试是有问题的,并且会破坏C++的构造函数/析构函数设计模式。

重写malloc/free然而,有一种更实际的方法可以重写malloc/firee。这是通过定义您自己的malloc/free函数来实现的。通常在调试过程中,这将使用宏在调用中包括FILELINE

#ifdef NDEBUG
#define myMalloc(s) myMallocImplementation(s,__FILE__,__LINE__);
#else
#define myMalloc(s) malloc(s)
#endif

这允许您的malloc实现保存分配内存的源位置。然而,这种方法不会捕获您正在使用的库中malloc/free的使用情况。这对于新的/删除调用来说有点困难,因为它通常需要在运行时对调用堆栈进行一定的挖掘,以找出谁调用了您的new()函数,这也是编译器特有的。另请参阅:MSDN博客文章

记忆冻结考虑到我喜欢的一切,我只想提到一些在安全关键代码中非常常见的东西(如机动车辆和/或飞机等)在初始化之外,安全关键程序通常不允许使用malloc/free/new/delete。因此,所有内存分配都必须在初始化期间进行,然后一旦程序以某种方式冻结,通常malloc/free就会被冻结。之后对malloc/free的任何调用都将导致断言。在C++环境中使用这可能是一个相当大的限制,但它确实会产生非常健壮的代码。请注意,这对缓冲区溢出访问或无效指针访问问题没有任何作用。