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, &current_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 时需要注意:

  1. 精度限制: 时间精度受系统时钟影响
  2. 平台差异: 某些字段在不同平台上可能不支持
  3. 累计统计: RUSAGE_CHILDREN 是累计值,不会重置
  4. 实时性: 数据反映的是到调用时刻的累计值
  5. 权限问题: 通常不需要特殊权限即可调用

总结 Link to heading

getrusage 是一个功能强大的系统调用,提供了全面的进程资源使用信息:

关键优势:

  1. 详细统计: 提供 CPU、内存、I/O 等多维度资源信息
  2. 灵活查询: 支持当前进程、子进程、线程等不同范围
  3. 性能分析: 是性能调优和基准测试的重要工具
  4. 系统监控: 适用于系统管理和监控应用

主要应用:

  1. 性能分析和基准测试工具
  2. 系统资源监控和管理
  3. 程序优化和调试
  4. 容器和虚拟化环境中的资源控制

正确使用 getrusage 可以帮助开发者深入了解程序的资源使用模式,是构建高效、可靠系统软件的重要工具。