如何在Linux/Ubuntu中获取进程精确的栈与堆内存占用?
栈与堆内存占用统计方案(针对嵌入式MCU验证)
一、栈内存统计问题解决
你遇到的/proc/pid/smaps显示4KB栈占用是因为Linux内存分页机制限制,无法精准到字节级。以下几种方法可满足需求:
1. 运行时动态追踪栈使用最大值
通过记录栈底与实时栈顶的差值,统计实际运行中的栈峰值:
#include <stdint.h> #include <stdio.h> uintptr_t stack_bottom; size_t max_stack_used = 0; // 检查当前栈使用量并更新峰值 void check_stack_usage(void) { uint8_t stack_top; size_t current_used = stack_bottom - (uintptr_t)&stack_top; if (current_used > max_stack_used) { max_stack_used = current_used; } } int main(void) { // 初始化栈底:main函数第一个局部变量地址(栈从高地址向低地址增长,多数架构适用) uint8_t bottom_marker; stack_bottom = (uintptr_t)&bottom_marker; // 在程序关键节点(如复杂逻辑执行前/后、函数调用边界)调用检查 check_stack_usage(); // ... 业务代码 // 程序结束时输出栈峰值 printf("Max Stack Used: %zu bytes\n", max_stack_used); return 0; }
2. 编译器静态分析栈需求
使用GCC的-fstack-usage编译选项,编译后生成.su文件,包含每个函数的静态栈使用量:
gcc -fstack-usage your_code.c -o your_program
.su文件示例内容:
your_code.c:16:6:func1 16 static your_code.c:22:6:func2 32 static
可编写脚本汇总所有函数的栈使用量,结合调用栈深度估算整体栈上限,适合CI快速静态检查。
3. 利用RTOS自带栈监控
若MCU使用RTOS(如FreeRTOS),多数RTOS提供栈监控API,例如FreeRTOS的uxTaskGetStackHighWaterMark(),可直接获取任务栈剩余空间,计算已使用的最大值。
二、堆内存统计优化(包含释放统计)
你当前的malloc包装仅统计分配,要统计释放,可通过以下两种方式实现:
1. 包装malloc/free(侵入式)
自定义函数替换原有malloc/free,记录每个块的大小:
#include <stdlib.h> #include <stdint.h> size_t total_allocated = 0; size_t total_freed = 0; size_t current_heap_used = 0; size_t max_heap_used = 0; void* call_malloc(size_t size) { // 额外分配空间存储块大小,放在返回指针前侧 size_t* ptr = malloc(size + sizeof(size_t)); if (ptr == NULL) return NULL; *ptr = size; total_allocated += size; current_heap_used += size; if (current_heap_used > max_heap_used) { max_heap_used = current_heap_used; } return (void*)(ptr + 1); } void call_free(void* ptr) { if (ptr == NULL) return; // 取出前侧的大小字段 size_t* size_ptr = (size_t*)((uint8_t*)ptr - sizeof(size_t)); size_t size = *size_ptr; total_freed += size; current_heap_used -= size; free(size_ptr); } // 用宏替换原有malloc/free,无需修改业务代码 #define malloc(size) call_malloc(size) #define free(ptr) call_free(ptr)
2. 使用malloc钩子(无侵入式,GCC扩展)
借助GCC的__malloc_hook和__free_hook,无需修改业务代码即可统计:
#include <stdlib.h> #include <malloc.h> size_t total_allocated = 0; size_t total_freed = 0; size_t current_heap_used = 0; size_t max_heap_used = 0; static void* my_malloc_hook(size_t size, const void* caller) { __malloc_hook = NULL; // 禁用钩子避免递归 void* ptr = malloc(size); if (ptr != NULL) { total_allocated += size; current_heap_used += size; if (current_heap_used > max_heap_used) { max_heap_used = current_heap_used; } } __malloc_hook = my_malloc_hook; return ptr; } static void my_free_hook(void* ptr, const void* caller) { __free_hook = NULL; if (ptr != NULL) { size_t size = malloc_usable_size(ptr); // 获取块实际可用大小 total_freed += size; current_heap_used -= size; } free(ptr); __free_hook = my_free_hook; } // 初始化钩子,建议在main开头调用 void init_heap_stats(void) { __malloc_hook = my_malloc_hook; __free_hook = my_free_hook; }
三、CI任务适配
- 栈检查:要么用
-fstack-usage生成.su文件,写脚本汇总栈需求并与30KB阈值对比;要么让程序运行时输出max_stack_used,CI捕获输出值做判断。 - 堆检查:程序运行结束时输出
max_heap_used、current_heap_used等指标,CI脚本读取这些值,验证是否在MCU内存限制内。
内容的提问来源于stack exchange,提问作者Galaxo
相关产品推荐
相关产品推荐

