深入探秘:Linux内存管理与泄漏检测

目录

1. 朋友,了解一下Linux的内存工作原理吧!

1.1. 这张图展示的是一个Linux进程的虚拟内存结构

2. 内存分配与回收:让你的程序跑得更稳健

2.1. 内存分配与内存泄漏

3. 内存泄漏检测代码分析

3.1. 预处理宏替换方法

3.2. 动态链接库挂钩方法

3.3. 代码具体解析

4. 动态链接库挂钩的具体操作

4.1. 创建一个动态链接库用于挂钩 malloc 和 free

4.2. 编写主程序

4.3. 运行程序并加载挂钩库

5. 两种方法的综合比较

6. 总结与建议


1. 朋友,了解一下Linux的内存工作原理吧!

朋友,你有没有遇到过这样的情况?你的Linux程序运行得挺顺利,但突然间发现内存占用飙升,系统变得卡顿,甚至有时候还会崩溃?别担心,今天我们就来聊聊Linux内存的工作原理,以及如何管理和监控内存,防止那些讨厌的内存泄漏。听起来有点复杂?别急,我会用最简单的方式,帮你把这些概念搞清楚。

1.1. 这张图展示的是一个Linux进程的虚拟内存结构

首先,让我们看看一个Linux进程的虚拟内存结构。内存被划分为多个区域,每个区域都有不同的用途。从下到上依次是:

  1. 代码段 (.text)

    • 这里保存了程序的可执行代码。当程序加载时,代码从磁盘读入并开始执行。
    • 所有进程共享相同的代码,通常是只读的,确保了代码的安全性和一致性。
  2. 已初始化数据段 (.data)

    • 这个区域存放已初始化的全局变量和静态变量。
    • 比如说,你在程序中定义的全局数组或静态计数器,就会被存放在这里。
  3. 未初始化数据段 (.bss)

    • 这里保存未初始化的全局变量和静态变量,初始值为0。
    • 运行时会动态分配内存给这些变量,确保它们在使用前被正确初始化。
  4. 运行时堆 (heap)

    • 堆区是用来动态分配内存的地方。每当你调用 malloccallocrealloc 时,内存就会从这里分配出来。
    • brk 指向堆的顶部,随着堆的扩展而增长。
    • 内存泄漏问题通常发生在这里。如果程序分配了内存却没有正确释放,堆区会不断增大,导致内存资源浪费。
  5. 共享库的内存映射区域

    • 这个区域用来存放共享库(比如 .so 文件)的内存映射。
    • 多个进程可以共享相同的内存区域,节省了系统资源。
  6. 用户栈 (stack)

    • 栈区用来存储函数调用时的局部变量和函数参数。
    • 每次函数调用都会在栈中压入一个新的帧,函数返回时弹出这个帧。
    • 栈是从高地址向低地址增长的,内存分配是自动的,由系统管理。
  7. 物理内存、内核代码和数据

    • 这是系统保留的区域,包括物理内存和与进程相关的数据结构(如页表、任务描述符等)。
    • 这些区域对用户进程是不可见的,确保了系统的稳定性和安全性。
  8. 内核虚拟内存

    • 这个区域保存了与内核相关的虚拟内存,供系统操作使用。
    • 它与用户空间是分离的,保证了内核操作的高效和安全。

2. 内存分配与回收:让你的程序跑得更稳健

内存分配与回收是程序运行过程中至关重要的部分。合理的内存管理不仅能提升程序的性能,还能防止那些讨厌的内存泄漏问题。让我们来看看内存分配与回收的基本流程吧!

2.1. 内存分配与内存泄漏

堆区是内存分配的核心。程序员可以通过调用 malloccallocrealloc 等函数,动态地分配和释放内存。然而,问题就出在这里——如果分配了内存却没有释放,或者在程序退出时忘记释放内存,这就会导致内存泄漏。

内存泄漏的危害

  • 内存堆逐渐增大:未释放的内存会让堆区不断膨胀,系统的可用内存减少。
  • 性能下降:内存资源被浪费,程序运行效率降低。
  • 系统崩溃:长时间运行的程序可能耗尽系统内存,导致程序或整个系统崩溃。

相比之下,栈区的内存分配是自动的,由系统管理。每次函数调用时分配内存,函数返回时自动释放。因此,栈区不会出现内存泄漏的问题。但要注意,栈溢出也是一个潜在的问题,尤其是在递归调用过多的情况下。

3. 内存泄漏检测代码分析

要确保你的程序不会出现内存泄漏,检测和监控内存的分配与释放是必不可少的。代码实现了两种内存监控方式,帮助我们检测内存泄漏:

  1. 预处理宏替换(通过宏定义替换 mallocfree
  2. 动态链接库挂钩(通过 dlsym 挂钩 mallocfree

让我们一起来看看这两种方法是如何工作的,以及它们各自的优缺点。

3.1. 预处理宏替换方法

实现原理

通过预处理宏,将代码中的 mallocfree 函数替换为自定义的 nMallocnFree 函数。这种方法在编译阶段完成替换,所有对 mallocfree 的调用都会被替换为带有额外参数(如文件名、函数名、行号)的自定义函数。

代码片段

#if 0

void *nMalloc(size_t size, const char *filename, const char *funcname, int line) {
    void *ptr = malloc(size);

    char buff[128] = {0};
    snprintf(buff, 128, "./block/%p.mem", ptr);

    FILE* fp = fopen(buff, "w");
    if (!fp) {
        free(ptr);
        return NULL;
    }

    fprintf(fp, "[+][%s:%s:%d] %p: %ld malloc\n", filename, funcname, line, ptr, size);
    fflush(fp);
    fclose(fp);

    return ptr;
}

void nFree(void *ptr, const char *filename, const char *funcname, int line) {
    char buff[128] = {0};
    snprintf(buff, 128, "./block/%p.mem", ptr);

    if (unlink(buff) < 0) { // 文件不存在
        printf("double free: %p at %s:%s:%d\n", ptr, filename, funcname, line);
        return;
    }

    return free(ptr);
}

#define malloc(size) nMalloc(size, __FILE__, __func__, __LINE__)
#define
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值