getrusage()获取子进程RSS值不准确的技术问题排查
问题原因及解决方法
核心原因
- 你用
fork()后调用shell执行目标程序时,getrusage(RUSAGE_CHILDREN)仅统计了shell进程本身的RSS,而非目标程序的内存使用。因为shell作为直接子进程,fork出目标程序后会快速退出,目标程序随即成为孤儿进程被init接管,不再属于当前进程的子进程组,因此RUSAGE_CHILDREN无法捕获到它的内存数据。 - Python的
subprocess默认会跟踪所有子进程(包括shell启动的子进程),且resource.getrusage在合适时机获取了整个子进程树的内存使用,所以结果准确。
解决方法
方法1:跳过shell直接执行目标程序
不通过shell中转,直接在fork()后的子进程里用execvp或execve执行目标程序,让目标程序成为直接子进程,getrusage(RUSAGE_CHILDREN)就能统计到它的内存数据:
#include <sys/wait.h> #include <sys/resource.h> #include <unistd.h> #include <iostream> int main() { pid_t pid = fork(); if (pid == 0) { // 直接执行目标程序,替换当前子进程 execvp("./target_program", nullptr); // exec失败才会走到这里 perror("execvp failed"); _exit(1); } else if (pid > 0) { wait(NULL); // 等待子进程退出 struct rusage usage; getrusage(RUSAGE_CHILDREN, &usage); std::cout << "RSS: " << usage.ru_maxrss << " KB" << std::endl; } else { perror("fork failed"); return 1; } return 0; }
方法2:跟踪整个子进程树(适用于必须通过shell执行的场景)
如果需要shell解析命令行参数、通配符等,可通过以下方式跟踪目标进程:
- 在shell启动目标程序时,获取目标进程的PID,用
waitpid等待它退出,再通过/proc/[pid]/statm或针对单个进程调用getrusage获取内存数据。 - 或使用
ptrace跟踪子进程创建,捕获所有子进程PID后统一统计内存使用(该方式实现复杂度较高)。
方法3:用wait4结合进程组等待
通过设置进程组,让shell和目标程序同属一个进程组,再用waitpid等待整个进程组退出,确保getrusage能统计到所有相关进程的内存:
#include <sys/wait.h> #include <sys/resource.h> #include <unistd.h> #include <iostream> int main() { pid_t pid = fork(); if (pid == 0) { // 设置进程组,让shell和目标程序同组 setpgid(0, 0); // 通过shell执行目标程序 execl("/bin/sh", "sh", "-c", "./target_program", nullptr); perror("execl failed"); _exit(1); } else if (pid > 0) { struct rusage usage; // 等待整个进程组退出 waitpid(-pid, NULL, 0); getrusage(RUSAGE_CHILDREN, &usage); std::cout << "RSS: " << usage.ru_maxrss << " KB" << std::endl; } else { perror("fork failed"); return 1; } return 0; }
内容的提问来源于stack exchange,提问作者Siddharth Mohanty
相关产品推荐
相关产品推荐

