84. getrusage - 获取进程资源使用情况 Link to heading
1. 函数介绍 Link to heading
getrusage
是一个 Linux 系统调用,用于获取进程及其子进程的资源使用统计信息。它提供了详细的 CPU 时间、内存使用、I/O 操作等资源消耗信息,是系统性能分析、资源监控和程序优化的重要工具。
2. 函数原型 Link to heading
#include <sys/resource.h>
int getrusage(int who, struct rusage *usage);
3. 功能 Link to heading
获取指定进程或进程组的资源使用统计信息,包括 CPU 时间、内存使用、页面错误、I/O 操作等各种系统资源的使用情况。
4. 参数 Link to heading
-
int who
: 指定要获取资源使用信息的进程范围RUSAGE_SELF
: 当前进程的资源使用情况RUSAGE_CHILDREN
: 当前进程所有已终止子进程的累计资源使用情况RUSAGE_THREAD
: 当前线程的资源使用情况(Linux 特有)
-
struct rusage *usage
: 指向rusage
结构体的指针,用于存储资源使用信息
5. rusage 结构体定义 Link to heading
struct rusage {
struct timeval ru_utime; /* 用户态 CPU 时间 */
struct timeval ru_stime; /* 内核态 CPU 时间 */
long ru_maxrss; /* 最大常驻集大小 (KB) */
long ru_ixrss; /* 共享内存大小 (字节*秒) */
long ru_idrss; /* 非共享数据/堆栈内存 (字节*秒) */
long ru_isrss; /* 栈使用的内存 (字节*秒) */
long ru_minflt; /* 页面错误次数(无需 I/O)*/
long ru_majflt; /* 页面错误次数(需要 I/O)*/
long ru_nswap; /* 交换次数 */
long ru_inblock; /* 块输入操作次数 */
long ru_oublock; /* 块输出操作次数 */
long ru_msgsnd; /* 发送的消息数 */
long ru_msgrcv; /* 接收的消息数 */
long ru_nsignals; /* 接收的信号数 */
long ru_nvcsw; /* 自愿上下文切换次数 */
long ru_nivcsw; /* 非自愿上下文切换次数 */
};
6. 返回值 Link to heading
- 成功时:返回 0
- 失败时:返回 -1,并设置
errno
7. 常见 errno 错误码 Link to heading
EFAULT
:usage
指针指向无效内存地址EINVAL
:who
参数无效
8. 相似函数,或关联函数 Link to heading
gettimeofday()
: 获取系统时间times()
: 获取进程时间信息clock_gettime()
: 获取高精度时间time()
: 获取当前时间getpid()
,getppid()
: 获取进程 ID 信息sysconf()
: 获取系统配置信息/proc/[pid]/stat
: 查看进程状态信息/proc/[pid]/status
: 查看进程详细状态ps
,top
: 系统监控命令
9. 示例代码 Link to heading
示例1:基本使用 - 获取进程资源使用情况 Link to heading
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/resource.h>
#include <errno.h>
#include <string.h>
void print_timeval(const char *label, const struct timeval *tv) {
printf("%-20s %ld.%06ld 秒\n", label, tv->tv_sec, tv->tv_usec);
}
void print_rusage(const char *label, const struct rusage *usage) {
printf("\n=== %s 资源使用情况 ===\n", label);
// CPU 时间
print_timeval("用户态 CPU 时间:", &usage->ru_utime);
print_timeval("内核态 CPU 时间:", &usage->ru_stime);
printf("%-20s %.6f 秒\n", "总 CPU 时间:",
usage->ru_utime.tv_sec + usage->ru_utime.tv_usec / 1000000.0 +
usage->ru_stime.tv_sec + usage->ru_stime.tv_usec / 1000000.0);
// 内存使用
printf("%-20s %ld KB\n", "最大内存使用:", usage->ru_maxrss);
// 页面错误
printf("%-20s %ld 次\n", "轻微页面错误:", usage->ru_minflt);
printf("%-20s %ld 次\n", "严重页面错误:", usage->ru_majflt);
printf("%-20s %ld 次\n", "总页面错误:", usage->ru_minflt + usage->ru_majflt);
// I/O 操作
printf("%-20s %ld 次\n", "块输入操作:", usage->ru_inblock);
printf("%-20s %ld 次\n", "块输出操作:", usage->ru_oublock);
// 上下文切换
printf("%-20s %ld 次\n", "自愿切换:", usage->ru_nvcsw);
printf("%-20s %ld 次\n", "非自愿切换:", usage->ru_nivcsw);
printf("%-20s %ld 次\n", "总切换次数:", usage->ru_nvcsw + usage->ru_nivcsw);
// 其他统计
printf("%-20s %ld 次\n", "交换次数:", usage->ru_nswap);
printf("%-20s %ld 个\n", "接收信号:", usage->ru_nsignals);
}
int main() {
struct rusage usage_self, usage_children;
int ret;
printf("=== 进程资源使用情况监控 ===\n");
// 获取当前进程资源使用情况
ret = getrusage(RUSAGE_SELF, &usage_self);
if (ret == -1) {
perror("获取当前进程资源使用情况失败");
exit(EXIT_FAILURE);
}
print_rusage("当前进程", &usage_self);
// 获取子进程资源使用情况
ret = getrusage(RUSAGE_CHILDREN, &usage_children);
if (ret == -1) {
perror("获取子进程资源使用情况失败");
} else {
print_rusage("子进程累计", &usage_children);
}
// 在 Linux 上还可以获取线程资源使用情况
#ifdef RUSAGE_THREAD
struct rusage usage_thread;
ret = getrusage(RUSAGE_THREAD, &usage_thread);
if (ret == 0) {
print_rusage("当前线程", &usage_thread);
}
#endif
return 0;
}
示例2:性能监控和基准测试 Link to heading
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <string.h>
#include <math.h>
typedef struct {
struct rusage start_usage;
struct rusage end_usage;
struct timeval start_time;
struct timeval end_time;
} performance_monitor_t;
int start_monitor(performance_monitor_t *monitor) {
if (getrusage(RUSAGE_SELF, &monitor->start_usage) == -1) {
return -1;
}
if (gettimeofday(&monitor->start_time, NULL) == -1) {
return -1;
}
return 0;
}
int stop_monitor(performance_monitor_t *monitor) {
if (getrusage(RUSAGE_SELF, &monitor->end_usage) == -1) {
return -1;
}
if (gettimeofday(&monitor->end_time, NULL) == -1) {
return -1;
}
return 0;
}
double timeval_diff(const struct timeval *end, const struct timeval *start) {
return (end->tv_sec - start->tv_sec) +
(end->tv_usec - start->tv_usec) / 1000000.0;
}
void print_performance_report(const performance_monitor_t *monitor) {
printf("\n=== 性能分析报告 ===\n");
// 时间统计
double wall_time = timeval_diff(&monitor->end_time, &monitor->start_time);
double user_time = timeval_diff(&monitor->end_usage.ru_utime,
&monitor->start_usage.ru_utime);
double system_time = timeval_diff(&monitor->end_usage.ru_stime,
&monitor->start_usage.ru_stime);
double cpu_time = user_time + system_time;
printf("时间统计:\n");
printf(" 墙钟时间: %.6f 秒\n", wall_time);
printf(" 用户时间: %.6f 秒\n", user_time);
printf(" 系统时间: %.6f 秒\n", system_time);
printf(" CPU 时间: %.6f 秒\n", cpu_time);
if (wall_time > 0) {
printf(" CPU 使用率: %.2f%%\n", (cpu_time / wall_time) * 100);
}
// 内存统计
long maxrss_diff = monitor->end_usage.ru_maxrss - monitor->start_usage.ru_maxrss;
printf("\n内存统计:\n");
printf(" 最大内存增长: %ld KB\n", maxrss_diff);
printf(" 最终内存使用: %ld KB\n", monitor->end_usage.ru_maxrss);
// 页面错误统计
long minflt_diff = monitor->end_usage.ru_minflt - monitor->start_usage.ru_minflt;
long majflt_diff = monitor->end_usage.ru_majflt - monitor->start_usage.ru_majflt;
printf("\n页面错误统计:\n");
printf(" 轻微页面错误: %ld 次\n", minflt_diff);
printf(" 严重页面错误: %ld 次\n", majflt_diff);
// I/O 统计
long inblock_diff = monitor->end_usage.ru_inblock - monitor->start_usage.ru_inblock;
long outblock_diff = monitor->end_usage.ru_oublock - monitor->start_usage.ru_oublock;
printf("\nI/O 统计:\n");
printf(" 块输入操作: %ld 次\n", inblock_diff);
printf(" 块输出操作: %ld 次\n", outblock_diff);
// 上下文切换统计
long vcsw_diff = monitor->end_usage.ru_nvcsw - monitor->start_usage.ru_nvcsw;
long ivcsw_diff = monitor->end_usage.ru_nivcsw - monitor->start_usage.ru_nivcsw;
printf("\n上下文切换统计:\n");
printf(" 自愿切换: %ld 次\n", vcsw_diff);
printf(" 非自愿切换: %ld 次\n", ivcsw_diff);
}
// 模拟一些计算密集型工作
void cpu_intensive_work() {
volatile double sum = 0;
for (int i = 0; i < 10000000; i++) {
sum += sqrt(i);
}
}
// 模拟一些内存分配工作
void memory_intensive_work() {
const size_t chunk_size = 1024 * 1024; // 1MB
const int num_chunks = 10;
char *chunks[num_chunks];
for (int i = 0; i < num_chunks; i++) {
chunks[i] = malloc(chunk_size);
if (chunks[i]) {
memset(chunks[i], i, chunk_size);
}
usleep(10000); // 10ms
}
// 释放内存
for (int i = 0; i < num_chunks; i++) {
if (chunks[i]) {
free(chunks[i]);
}
}
}
int main() {
performance_monitor_t monitor;
printf("=== 性能基准测试 ===\n");
// 开始监控
if (start_monitor(&monitor) == -1) {
perror("启动监控失败");
exit(EXIT_FAILURE);
}
printf("执行 CPU 密集型工作...\n");
cpu_intensive_work();
printf("执行内存密集型工作...\n");
memory_intensive_work();
// 停止监控
if (stop_monitor(&monitor) == -1) {
perror("停止监控失败");
exit(EXIT_FAILURE);
}
// 输出性能报告
print_performance_report(&monitor);
return 0;
}
示例3:子进程资源使用监控 Link to heading
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
void print_process_resources(const char *label) {
struct rusage usage;
printf("\n%s 资源使用情况:\n", label);
// 当前进程
if (getrusage(RUSAGE_SELF, &usage) == 0) {
printf(" 当前进程用户时间: %ld.%06ld 秒\n",
usage.ru_utime.tv_sec, usage.ru_utime.tv_usec);
printf(" 当前进程系统时间: %ld.%06ld 秒\n",
usage.ru_stime.tv_sec, usage.ru_stime.tv_usec);
printf(" 当前进程内存使用: %ld KB\n", usage.ru_maxrss);
}
// 子进程累计
if (getrusage(RUSAGE_CHILDREN, &usage) == 0) {
printf(" 子进程累计用户时间: %ld.%06ld 秒\n",
usage.ru_utime.tv_sec, usage.ru_utime.tv_usec);
printf(" 子进程累计系统时间: %ld.%06ld 秒\n",
usage.ru_stime.tv_sec, usage.ru_stime.tv_usec);
printf(" 子进程累计内存使用: %ld KB\n", usage.ru_maxrss);
}
}
void child_work(int child_id) {
struct rusage usage_start, usage_end;
// 获取初始资源使用情况
getrusage(RUSAGE_SELF, &usage_start);
printf("子进程 %d 开始工作\n", child_id);
// 模拟一些工作
volatile long sum = 0;
for (int i = 0; i < 5000000; i++) {
sum += i;
if (i % 1000000 == 0) {
usleep(100000); // 100ms
}
}
// 获取结束资源使用情况
getrusage(RUSAGE_SELF, &usage_end);
// 计算资源使用差异
long user_time_ms = (usage_end.ru_utime.tv_sec - usage_start.ru_utime.tv_sec) * 1000 +
(usage_end.ru_utime.tv_usec - usage_start.ru_utime.tv_usec) / 1000;
long system_time_ms = (usage_end.ru_stime.tv_sec - usage_start.ru_stime.tv_sec) * 1000 +
(usage_end.ru_stime.tv_usec - usage_start.ru_stime.tv_usec) / 1000;
printf("子进程 %d 完成:\n", child_id);
printf(" 用户时间: %ld ms\n", user_time_ms);
printf(" 系统时间: %ld ms\n", system_time_ms);
printf(" 内存使用: %ld KB\n", usage_end.ru_maxrss);
exit(child_id);
}
int main() {
pid_t children[3];
int child_count = 3;
printf("=== 子进程资源使用监控 ===\n");
// 显示初始状态
print_process_resources("父进程初始");
// 创建子进程
for (int i = 0; i < child_count; i++) {
children[i] = fork();
if (children[i] == -1) {
perror("创建子进程失败");
exit(EXIT_FAILURE);
}
if (children[i] == 0) {
// 子进程
child_work(i + 1);
}
}
// 父进程等待子进程完成
printf("父进程等待所有子进程完成...\n");
int total_status = 0;
for (int i = 0; i < child_count; i++) {
int status;
pid_t finished_pid = waitpid(children[i], &status, 0);
if (finished_pid > 0) {
if (WIFEXITED(status)) {
int exit_code = WEXITSTATUS(status);
printf("子进程 %d 正常退出,退出码: %d\n", finished_pid, exit_code);
total_status += exit_code;
} else if (WIFSIGNALED(status)) {
printf("子进程 %d 被信号终止,信号: %d\n", finished_pid, WTERMSIG(status));
}
}
}
// 显示最终状态
print_process_resources("父进程最终");
printf("\n所有子进程完成,总计退出码: %d\n", total_status);
return 0;
}
示例4:资源使用限制和监控工具 Link to heading
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/resource.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
volatile sig_atomic_t monitoring = 1;
void signal_handler(int sig) {
monitoring = 0;
printf("\n收到信号 %d,停止监控\n", sig);
}
typedef struct {
long max_memory_kb;
long max_cpu_seconds;
long total_page_faults;
long total_io_operations;
} resource_limits_t;
int check_resource_limits(const struct rusage *usage, const resource_limits_t *limits) {
// 检查内存使用
if (limits->max_memory_kb > 0 && usage->ru_maxrss > limits->max_memory_kb) {
printf("警告: 内存使用超过限制 (%ld KB > %ld KB)\n",
usage->ru_maxrss, limits->max_memory_kb);
return 1;
}
// 检查 CPU 时间
if (limits->max_cpu_seconds > 0) {
long total_cpu_time = usage->ru_utime.tv_sec + usage->ru_stime.tv_sec;
if (total_cpu_time > limits->max_cpu_seconds) {
printf("警告: CPU 时间超过限制 (%ld 秒 > %ld 秒)\n",
total_cpu_time, limits->max_cpu_seconds);
return 1;
}
}
return 0;
}
void monitor_resources(int duration_seconds, const resource_limits_t *limits) {
struct rusage usage;
time_t start_time = time(NULL);
time_t current_time;
int sample_count = 0;
printf("开始监控资源使用情况 (%d 秒)...\n", duration_seconds);
printf("%-8s %-10s %-10s %-12s %-12s %-10s\n",
"时间", "内存(KB)", "用户(s)", "系统(s)", "页面错误", "I/O操作");
printf("%-8s %-10s %-10s %-12s %-12s %-10s\n",
"----", "-------", "------", "------", "--------", "------");
while (monitoring && (current_time = time(NULL)) - start_time < duration_seconds) {
if (getrusage(RUSAGE_SELF, &usage) == 0) {
long user_time = usage.ru_utime.tv_sec;
long system_time = usage.ru_stime.tv_sec;
long total_faults = usage.ru_minflt + usage.ru_majflt;
long total_io = usage.ru_inblock + usage.ru_oublock;
printf("%-8ld %-10ld %-10ld %-12ld %-12ld %-10ld\n",
current_time - start_time,
usage.ru_maxrss,
user_time,
system_time,
total_faults,
total_io);
// 检查资源限制
if (limits && check_resource_limits(&usage, limits)) {
printf("资源限制检查失败,继续监控...\n");
}
sample_count++;
}
sleep(1);
}
printf("监控结束,共采样 %d 次\n", sample_count);
}
void set_resource_limits() {
struct rlimit limit;
// 设置 CPU 时间限制(30秒)
limit.rlim_cur = 30;
limit.rlim_max = 60;
if (setrlimit(RLIMIT_CPU, &limit) == 0) {
printf("已设置 CPU 时间限制: 软限制 30 秒,硬限制 60 秒\n");
}
// 设置内存限制(100MB)
limit.rlim_cur = 100 * 1024 * 1024;
limit.rlim_max = 200 * 1024 * 1024;
if (setrlimit(RLIMIT_AS, &limit) == 0) {
printf("已设置内存限制: 软限制 100MB,硬限制 200MB\n");
}
}
int main() {
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
printf("=== 资源使用监控和限制工具 ===\n");
// 设置资源限制
set_resource_limits();
// 定义资源限制
resource_limits_t limits = {
.max_memory_kb = 50000, // 50MB
.max_cpu_seconds = 10, // 10秒
.max_page_faults = 0, // 不限制
.max_io_operations = 0 // 不限制
};
// 开始监控
printf("开始 15 秒资源监控...\n");
monitor_resources(15, &limits);
// 显示最终资源使用情况
struct rusage final_usage;
if (getrusage(RUSAGE_SELF, &final_usage) == 0) {
printf("\n最终资源使用情况:\n");
printf(" 最大内存使用: %ld KB\n", final_usage.ru_maxrss);
printf(" 用户 CPU 时间: %ld.%06ld 秒\n",
final_usage.ru_utime.tv_sec, final_usage.ru_utime.tv_usec);
printf(" 系统 CPU 时间: %ld.%06ld 秒\n",
final_usage.ru_stime.tv_sec, final_usage.ru_stime.tv_usec);
printf(" 总页面错误: %ld 次\n",
final_usage.ru_minflt + final_usage.ru_majflt);
printf(" 总 I/O 操作: %ld 次\n",
final_usage.ru_inblock + final_usage.ru_oublock);
}
return 0;
}
10. 资源统计字段详细说明 Link to heading
// CPU 时间相关
ru_utime // 用户态 CPU 时间(程序代码执行时间)
ru_stime // 内核态 CPU 时间(系统调用时间)
// 内存使用相关
ru_maxrss // 最大常驻集大小(实际使用的物理内存)
ru_ixrss // 共享内存大小(通常为 0)
ru_idrss // 非共享数据内存(通常为 0)
ru_isrss // 栈内存使用(通常为 0)
// 页面错误相关
ru_minflt // 轻微页面错误(无需磁盘 I/O)
ru_majflt // 严重页面错误(需要磁盘 I/O)
// I/O 操作相关
ru_inblock // 块输入操作次数
ru_oublock // 块输出操作次数
// 进程间通信相关
ru_msgsnd // 发送的消息数
ru_msgrcv // 接收的消息数
// 其他统计
ru_nswap // 交换次数
ru_nsignals // 接收的信号数
ru_nvcsw // 自愿上下文切换(等待资源)
ru_nivcsw // 非自愿上下文切换(时间片用完)
11. 实际应用场景 Link to heading
场景1:性能分析工具 Link to heading
void analyze_program_performance() {
struct rusage before, after;
getrusage(RUSAGE_SELF, &before);
// 执行被分析的代码
execute_target_code();
getrusage(RUSAGE_SELF, &after);
// 分析性能差异
long cpu_time_ms = (after.ru_utime.tv_sec - before.ru_utime.tv_sec) * 1000 +
(after.ru_utime.tv_usec - before.ru_utime.tv_usec) / 1000;
printf("CPU 时间增加: %ld ms\n", cpu_time_ms);
}
场景2:资源限制检查 Link to heading
int check_memory_leak() {
static struct rusage last_usage = {0};
struct rusage current_usage;
if (getrusage(RUSAGE_SELF, ¤t_usage) == 0) {
if (current_usage.ru_maxrss > last_usage.ru_maxrss + 10000) { // 增加 10MB
printf("警告: 可能存在内存泄漏\n");
last_usage = current_usage;
return 1;
}
last_usage = current_usage;
}
return 0;
}
场景3:系统监控 Link to heading
void log_process_resources() {
struct rusage usage;
if (getrusage(RUSAGE_SELF, &usage) == 0) {
syslog(LOG_INFO, "进程资源使用 - 内存: %ldKB, CPU: %ld.%06lds+%ld.%06lds",
usage.ru_maxrss,
usage.ru_utime.tv_sec, usage.ru_utime.tv_usec,
usage.ru_stime.tv_sec, usage.ru_stime.tv_usec);
}
}
12. 注意事项 Link to heading
使用 getrusage
时需要注意:
- 精度限制: 时间精度受系统时钟影响
- 平台差异: 某些字段在不同平台上可能不支持
- 累计统计:
RUSAGE_CHILDREN
是累计值,不会重置 - 实时性: 数据反映的是到调用时刻的累计值
- 权限问题: 通常不需要特殊权限即可调用
总结 Link to heading
getrusage
是一个功能强大的系统调用,提供了全面的进程资源使用信息:
关键优势:
- 详细统计: 提供 CPU、内存、I/O 等多维度资源信息
- 灵活查询: 支持当前进程、子进程、线程等不同范围
- 性能分析: 是性能调优和基准测试的重要工具
- 系统监控: 适用于系统管理和监控应用
主要应用:
- 性能分析和基准测试工具
- 系统资源监控和管理
- 程序优化和调试
- 容器和虚拟化环境中的资源控制
正确使用 getrusage
可以帮助开发者深入了解程序的资源使用模式,是构建高效、可靠系统软件的重要工具。