为何Dtruss无法捕获mmap/sbrk系统调用?
嘿,这个问题我之前也碰到过,刚好可以给你拆解下为啥会出现这种反直觉的情况~
为啥你没看到预期的mmap/sbrk系统调用?
1. 用户态内存分配器的“缓存魔法”
现代C/C++标准库的内存分配器(比如macOS的libmalloc、Linux的glibc ptmalloc)都不会傻到每次new或者实例化对象就直接找内核要内存:
- 第一次申请内存时,分配器会通过
mmap或sbrk向内核批量申请一大块内存(通常是几MB级别),存在自己的用户态缓存池里 - 后续所有的
new操作、LinkedList节点实例化,都是直接从这块缓存里划分小内存块,全程在用户态完成,完全不需要触发系统调用 - 只有当缓存耗尽、要分配超大内存块,或者缓存里空闲内存太多要还给内核时,才会再次调用
mmap/sbrk这类内核接口
2. dtruss的追踪细节容易踩坑
你提到用-s参数运行dtruss,这里有几个容易忽略的点:
-s是显示系统调用的栈跟踪,但默认dtruss可能不会追踪所有线程的调用(如果你的代码用到了多线程)- macOS上的dtruss默认会过滤部分系统调用,或者需要加
-f参数追踪fork出的子进程(不过你的LinkedList应该是单进程) - 最好直接用
dtruss ./your_linkedlist_program来启动并追踪,避免漏过程序初始化阶段的系统调用 - 另外,Big Sur之后的macOS有SIP限制,不关闭的话dtruss可能无法追踪全部内核操作,会漏掉一些调用
3. 小内存分配的特殊优化
如果你的LinkedList节点很小(比如只存一个int+两个指针),分配器会用更高效的slab分配或内存池策略,完全在用户态完成内存划分,连碰内核的机会都没有。只有当你分配超大内存块(比如几MB级别的),才会直接走mmap申请匿名映射。
怎么验证这个逻辑?
给你几个实操方法:
- 一次性创建10万+个LinkedList节点,此时缓存肯定不够用,就能看到
mmap/sbrk触发了 - 手动调用分配器的清理接口,强制把空闲内存还给内核:
调用后再做内存分配,就能看到系统调用了// macOS 下 #include <malloc/malloc.h> malloc_zone_purge(malloc_default_zone()); // Linux 下 #include <malloc.h> malloc_trim(0); - 用分配器自带的统计工具查看缓存状态,比如在代码里加:
运行后会输出分配器从内核申请的总内存、用户态缓存的空闲内存等数据,一目了然#include <malloc/malloc.h> // macOS // #include <malloc.h> // Linux malloc_stats();
小提示:如果还是看不到,试试把程序的内存分配量拉到足够大,比如直接分配一个10MB的数组,肯定能触发
mmap。
内容的提问来源于stack exchange,提问作者Lazycrow
相关产品推荐
相关产品推荐

