getrusage是posix系统调用,用于获取进程资源使用统计;需包含,rusage_self查自身,rusage_children查已终止子进程;ru_maxrss在linux单位为kb、macos为bytes,须平台适配;封装时应避免析构重调、信号处理中调用及依赖废弃字段。

Linux/macOS 下用 getrusage 获取进程资源使用统计
getrusage 是 POSIX 提供的系统调用,能拿到当前进程(或子进程)的详细资源消耗,比如用户态/内核态 CPU 时间、内存峰值、上下文切换次数等。它不跨平台——Windows 上没有原生对应接口,别指望在 MSVC 里直接用。
调用前需包含头文件:#include <sys></sys>,并链接默认 libc(无需额外 -l 选项)。关键在于传入正确的 who 参数:RUSAGE_SELF 查自己,RUSAGE_CHILDREN 查已终止的子进程累计值(注意:fork 出来但还没 wait 的子进程不算在内)。
- 返回值为 int,成功返回 0,失败返回 -1 并设置
errno(常见如EFAULT表示ru指针非法) -
struct rusage中的ru_maxrss单位是 KB(Linux)或 bytes(macOS),必须检查运行平台,不能硬编码换算逻辑 -
ru_utime和ru_stime是struct timeval,分别表示用户态和内核态耗时,相加才是总 CPU 时间
ru_maxrss 在不同系统上的单位差异与读取陷阱
这是最容易出错的地方:Linux 返回 KB,macOS 返回字节。如果统一除以 1024 当作 KB 处理,在 macOS 上会把真实值砍掉 1024 倍,导致内存峰值看起来小得离谱。
没有标准宏可直接判断,稳妥做法是构建时探测或运行时根据 uname() 区分:
#include <sys>
bool is_macos() {
struct utsname u;
return uname(&u) == 0 && std::string(u.sysname) == "Darwin";
}
</sys>
或者更轻量——只在需要打印/比较时动态适配,避免全局状态污染。
- Linux 上
ru_maxrss是进程生命周期内的物理内存峰值(RSS),不是虚拟内存,也不含共享库占用(因按需映射) - 该值不会实时更新,只在内核认为有必要时刷新(例如发生 major page fault 后),所以两次调用间隔太短可能看到相同数值
- 容器环境(如 Docker)中,cgroup 限制可能使
ru_maxrss达不到宿主机物理内存上限,但getrusage本身不感知 cgroup,它报告的是实际使用的物理页数
如何在 C++ 中安全封装 getrusage 调用
裸调 getrusage 容易漏错误检查,也难复用。建议封装成一个轻量结构体,构造时自动采集,提供毫秒级 CPU 时间、MB 级内存峰值等常用视图:
struct ProcUsage {
rusage ru;
ProcUsage(int who = RUSAGE_SELF) { getrusage(who, &ru); }
long cpu_ms() const { return (ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) * 1000 +
(ru.ru_utime.tv_usec + ru.ru_stime.tv_usec) / 1000; }
size_t max_rss_mb() const {
long kb = ru.ru_maxrss;
#ifdef __APPLE__
kb /= 1024; // macOS 返回 bytes
#endif
return static_cast<size_t>(kb / 1024);
}
};
</size_t>
注意:不要在析构函数里再调一次 getrusage 做“差值”——这既非原子操作,也无法排除中间有其他线程触发分配或释放的影响。
- 若需测量某段代码的开销,应在前后各调一次并手工做减法,且确保单线程执行(或多线程下加锁+注意
RUSAGE_SELF是整个进程的汇总) - 避免在信号处理函数中调用
getrusage,它不是 async-signal-safe 函数 - glibc 2.28+ 在某些架构上对
ru_idrss/ru_isrss等字段返回 0,这些字段早已废弃,别依赖它们
替代方案:为什么有时候不该用 getrusage
当需要更高频采样(如每毫秒)、或想获取线程粒度、或需监控虚拟内存/文件描述符/锁竞争等维度时,getrusage 就力不从心了。它本质是内核快照,开销低但信息粒度粗。
生产环境更常见的是组合手段:
- 读取
/proc/self/status(Linux)或/proc/self/rusage(部分新版内核)获取更细字段,比如VmPeak、VmRSS、FDSize - 用
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...)获取更精确的 CPU 时间(纳秒级,且不受系统时间调整影响) - 对内存分配敏感的场景,用
mallinfo或malloc_stats()(glibc)观察堆行为,但它不包含 mmap 分配的内存
真正卡点往往不在“怎么取”,而在“取哪一时刻”——比如程序崩溃前最后一刻的 ru_maxrss 值,getrusage 本身无法帮你捕获,得靠 signal handler + sigaltstack 配合,而这又引入更多限制和风险。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











