81. getpriority - 获取进程或进程组的优先级 Link to heading

1. 函数介绍 Link to heading

getpriority 是一个 Linux 系统调用,用于获取指定进程或进程组的调度优先级。它是 Unix/Linux 系统中进程调度管理的重要组成部分,允许程序查询进程的当前优先级设置。

在 Linux 系统中,进程优先级影响 CPU 调度,优先级高的进程会获得更多 CPU 时间。getprioritysetpriority 配合使用,提供了完整的优先级管理功能。

2. 函数原型 Link to heading

#include <sys/resource.h>

int getpriority(int which, id_t who);

3. 功能 Link to heading

获取指定进程、进程组或用户的所有进程的调度优先级。优先级值范围通常为 -20 到 +19,其中 -20 表示最高优先级,+19 表示最低优先级。

4. 参数 Link to heading

  • int which: 指定查询的类型

    • PRIO_PROCESS: 查询单个进程的优先级
    • PRIO_PGRP: 查询进程组中所有进程的优先级
    • PRIO_USER: 查询指定用户的所有进程的优先级
  • id_t who: 根据 which 参数指定的具体 ID

    • PRIO_PROCESS: 进程 ID(0 表示当前进程)
    • PRIO_PGRP: 进程组 ID(0 表示当前进程组)
    • PRIO_USER: 用户 ID(0 表示当前用户)

5. 返回值 Link to heading

  • 成功时:返回优先级值(范围 -20 到 +19)
  • 失败时:返回 -1,并设置 errno
  • 注意:由于成功时可能返回 -1,需要先清除 errno 再调用

6. 常见 errno 错误码 Link to heading

  • ESRCH: 指定的进程、进程组或用户不存在
  • EINVAL: which 参数无效
  • EPERM: 权限不足(无法访问指定进程的信息)
  • EACCES: 访问被拒绝(某些安全策略下)

7. 相似函数,或关联函数 Link to heading

  • setpriority(): 设置进程优先级
  • nice(): 调整当前进程的优先级
  • getrlimit(), setrlimit(): 获取/设置资源限制
  • sched_getparam(), sched_setparam(): 更高级的调度参数管理
  • sched_getscheduler(), sched_setscheduler(): 获取/设置调度策略
  • getrusage(): 获取进程资源使用情况
  • /proc/[pid]/stat: 查看进程状态信息

8. 示例代码 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>

int safe_getpriority(int which, id_t who) {
    errno = 0;  // 必须先清除 errno
    int priority = getpriority(which, who);
    if (priority == -1 && errno != 0) {
        return -1;  // 真正的错误
    }
    return priority;
}

int main() {
    int priority;
    
    printf("=== 进程优先级获取 ===\n");
    
    // 获取当前进程的优先级
    priority = safe_getpriority(PRIO_PROCESS, 0);
    if (priority == -1) {
        perror("获取当前进程优先级失败");
    } else {
        printf("当前进程优先级: %d\n", priority);
    }
    
    // 获取当前进程组的优先级
    priority = safe_getpriority(PRIO_PGRP, 0);
    if (priority == -1) {
        perror("获取当前进程组优先级失败");
    } else {
        printf("当前进程组优先级: %d\n", priority);
    }
    
    // 获取当前用户的进程优先级
    priority = safe_getpriority(PRIO_USER, 0);
    if (priority == -1) {
        perror("获取当前用户进程优先级失败");
    } else {
        printf("当前用户进程优先级: %d\n", priority);
    }
    
    // 获取 init 进程的优先级
    priority = safe_getpriority(PRIO_PROCESS, 1);
    if (priority == -1) {
        if (errno == EPERM) {
            printf("权限不足,无法获取 init 进程优先级\n");
        } else {
            perror("获取 init 进程优先级失败");
        }
    } else {
        printf("init 进程优先级: %d\n", priority);
    }
    
    return 0;
}

示例2:错误处理和特殊情况 Link to heading

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/resource.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>

void test_getpriority(int which, id_t who, const char *description) {
    printf("\n测试 %s:\n", description);
    
    errno = 0;  // 清除 errno
    int priority = getpriority(which, who);
    
    if (priority == -1 && errno != 0) {
        printf("  getpriority 调用失败: %s\n", strerror(errno));
        switch (errno) {
            case ESRCH:
                printf("  原因: 指定的进程/组/用户不存在\n");
                break;
            case EINVAL:
                printf("  原因: 无效的 which 参数\n");
                break;
            case EPERM:
                printf("  原因: 权限不足\n");
                break;
            default:
                printf("  其他错误\n");
                break;
        }
    } else {
        printf("  优先级: %d\n", priority);
    }
}

int main() {
    printf("=== getpriority 错误处理测试 ===\n");
    
    // 测试正常情况
    test_getpriority(PRIO_PROCESS, 0, "当前进程");
    test_getpriority(PRIO_PGRP, 0, "当前进程组");
    test_getpriority(PRIO_USER, getuid(), "当前用户");
    
    // 测试无效参数
    test_getpriority(999, 0, "无效的 which 参数");
    
    // 测试不存在的进程
    test_getpriority(PRIO_PROCESS, 999999, "不存在的进程");
    
    // 测试不存在的进程组
    test_getpriority(PRIO_PGRP, 999999, "不存在的进程组");
    
    // 创建子进程进行测试
    pid_t child_pid = fork();
    if (child_pid == -1) {
        perror("fork 失败");
        exit(EXIT_FAILURE);
    }
    
    if (child_pid == 0) {
        // 子进程
        printf("\n=== 子进程信息 ===\n");
        printf("子进程 ID: %d\n", getpid());
        printf("父进程 ID: %d\n", getppid());
        
        // 子进程设置自己的优先级
        if (setpriority(PRIO_PROCESS, 0, 10) == 0) {
            printf("子进程成功设置优先级为 10\n");
        } else {
            perror("子进程设置优先级失败");
        }
        
        // 显示子进程优先级
        errno = 0;
        int child_priority = getpriority(PRIO_PROCESS, 0);
        if (!(child_priority == -1 && errno != 0)) {
            printf("子进程当前优先级: %d\n", child_priority);
        }
        
        // 子进程睡眠一段时间
        sleep(3);
        exit(0);
    } else {
        // 父进程
        printf("\n=== 父进程测试子进程 ===\n");
        printf("子进程 ID: %d\n", child_pid);
        
        // 父进程获取子进程优先级
        test_getpriority(PRIO_PROCESS, child_pid, "子进程(运行中)");
        
        // 等待子进程结束
        int status;
        waitpid(child_pid, &status, 0);
        printf("子进程已结束\n");
        
        // 再次测试已结束的进程
        test_getpriority(PRIO_PROCESS, child_pid, "已结束的子进程");
    }
    
    return 0;
}

示例3:优先级监控和管理工具 Link to heading

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/resource.h>
#include <errno.h>
#include <string.h>
#include <pwd.h>
#include <grp.h>

typedef struct {
    pid_t pid;
    int priority;
    char process_name[256];
} process_priority_t;

int safe_getpriority(int which, id_t who) {
    errno = 0;
    int priority = getpriority(which, who);
    if (priority == -1 && errno != 0) {
        return -999;  // 特殊错误值
    }
    return priority;
}

void print_priority_info(const char *label, int priority) {
    if (priority == -999) {
        printf("%s: 无法获取\n", label);
    } else {
        printf("%s: %d", label, priority);
        if (priority < 0) {
            printf(" (高优先级)");
        } else if (priority > 0) {
            printf(" (低优先级)");
        } else {
            printf(" (正常优先级)");
        }
        printf("\n");
    }
}

void analyze_process_priorities() {
    printf("=== 进程优先级分析 ===\n");
    
    // 当前进程信息
    printf("当前进程信息:\n");
    printf("  PID: %d\n", getpid());
    printf("  UID: %d", getuid());
    struct passwd *pwd = getpwuid(getuid());
    if (pwd) {
        printf(" (%s)", pwd->pw_name);
    }
    printf("\n");
    
    printf("  GID: %d", getgid());
    struct group *grp = getgrgid(getgid());
    if (grp) {
        printf(" (%s)", grp->gr_name);
    }
    printf("\n");
    
    // 各种优先级信息
    int current_priority = safe_getpriority(PRIO_PROCESS, 0);
    print_priority_info("  当前进程优先级", current_priority);
    
    int pgrp_priority = safe_getpriority(PRIO_PGRP, 0);
    print_priority_info("  当前进程组优先级", pgrp_priority);
    
    int user_priority = safe_getpriority(PRIO_USER, getuid());
    print_priority_info("  当前用户进程优先级", user_priority);
    
    // 系统关键进程优先级
    printf("\n系统关键进程优先级:\n");
    
    int init_priority = safe_getpriority(PRIO_PROCESS, 1);
    print_priority_info("  init 进程 (PID 1)", init_priority);
    
    // 获取 shell 进程优先级
    pid_t shell_pid = getppid();
    int shell_priority = safe_getpriority(PRIO_PROCESS, shell_pid);
    printf("  父进程 (shell) PID %d", shell_pid);
    print_priority_info("", shell_priority);
}

void priority_statistics() {
    printf("\n=== 优先级统计信息 ===\n");
    
    // 获取当前用户的进程优先级范围
    int min_priority = 20, max_priority = -20;
    int sum_priority = 0, count = 0;
    
    // 这里简化处理,实际应用中可能需要扫描 /proc
    int current_priority = safe_getpriority(PRIO_PROCESS, 0);
    if (current_priority != -999) {
        min_priority = max_priority = sum_priority = current_priority;
        count = 1;
    }
    
    printf("当前会话优先级统计:\n");
    printf("  进程数量: %d\n", count);
    if (count > 0) {
        printf("  最低优先级: %d\n", min_priority);
        printf("  最高优先级: %d\n", max_priority);
        printf("  平均优先级: %.2f\n", (double)sum_priority / count);
    }
}

int main() {
    analyze_process_priorities();
    priority_statistics();
    
    // 交互式优先级查询
    printf("\n=== 交互式查询 ===\n");
    printf("输入进程 ID 查询优先级 (输入 0 退出): ");
    
    pid_t target_pid;
    while (scanf("%d", &target_pid) == 1 && target_pid != 0) {
        if (target_pid > 0) {
            int priority = safe_getpriority(PRIO_PROCESS, target_pid);
            if (priority != -999) {
                printf("进程 %d 的优先级: %d\n", target_pid, priority);
            } else {
                printf("无法获取进程 %d 的优先级\n", target_pid);
            }
        }
        printf("继续输入进程 ID (输入 0 退出): ");
    }
    
    return 0;
}

示例4:优先级调整和监控 Link to heading

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/resource.h>
#include <errno.h>
#include <string.h>
#include <signal.h>

volatile sig_atomic_t running = 1;

void signal_handler(int sig) {
    running = 0;
    printf("\n收到信号 %d,停止监控\n", sig);
}

int safe_getpriority(int which, id_t who) {
    errno = 0;
    int priority = getpriority(which, who);
    if (priority == -1 && errno != 0) {
        return -999;
    }
    return priority;
}

void monitor_priority_changes(pid_t target_pid, int duration) {
    int last_priority = -999;
    time_t start_time = time(NULL);
    time_t current_time;
    
    printf("开始监控进程 %d 的优先级变化 (%d 秒)...\n", target_pid, duration);
    printf("%-10s %-12s %s\n", "时间", "优先级", "状态");
    printf("%-10s %-12s %s\n", "----", "----", "----");
    
    while (running && (current_time = time(NULL)) - start_time < duration) {
        int current_priority = safe_getpriority(PRIO_PROCESS, target_pid);
        
        if (current_priority != -999) {
            char status[20] = "稳定";
            
            if (last_priority != -999 && current_priority != last_priority) {
                snprintf(status, sizeof(status), "变化 %d->%d", 
                        last_priority, current_priority);
            }
            
            printf("%-10ld %-12d %s\n", 
                   current_time - start_time, 
                   current_priority, 
                   status);
            
            last_priority = current_priority;
        } else {
            printf("%-10ld %-12s %s\n", 
                   current_time - start_time, 
                   "无法获取", 
                   "错误");
        }
        
        sleep(1);
    }
}

void demonstrate_priority_adjustment() {
    printf("=== 优先级调整演示 ===\n");
    
    int original_priority = safe_getpriority(PRIO_PROCESS, 0);
    printf("原始优先级: %d\n", original_priority);
    
    // 提高优先级(需要权限)
    printf("尝试提高优先级到 -5...\n");
    if (setpriority(PRIO_PROCESS, 0, -5) == 0) {
        int new_priority = safe_getpriority(PRIO_PROCESS, 0);
        printf("优先级调整成功: %d\n", new_priority);
    } else {
        if (errno == EPERM) {
            printf("权限不足,无法提高优先级(需要 root 权限)\n");
        } else {
            perror("优先级调整失败");
        }
    }
    
    // 降低优先级
    printf("尝试降低优先级到 10...\n");
    if (setpriority(PRIO_PROCESS, 0, 10) == 0) {
        int new_priority = safe_getpriority(PRIO_PROCESS, 0);
        printf("优先级调整成功: %d\n", new_priority);
    } else {
        perror("优先级调整失败");
    }
    
    // 恢复原始优先级
    if (original_priority != -999) {
        if (setpriority(PRIO_PROCESS, 0, original_priority) == 0) {
            printf("已恢复原始优先级: %d\n", 
                   safe_getpriority(PRIO_PROCESS, 0));
        }
    }
}

int main() {
    signal(SIGINT, signal_handler);
    signal(SIGTERM, signal_handler);
    
    demonstrate_priority_adjustment();
    
    printf("\n当前进程优先级: %d\n", safe_getpriority(PRIO_PROCESS, 0));
    
    // 如果需要,可以监控优先级变化
    char choice;
    printf("\n是否监控当前进程优先级变化? (y/N): ");
    getchar();  // 清除缓冲区
    if (scanf("%c", &choice) == 1 && (choice == 'y' || choice == 'Y')) {
        monitor_priority_changes(getpid(), 10);
    }
    
    return 0;
}

9. 优先级值说明 Link to heading

Linux 进程优先级系统:

// 优先级范围:-20 到 +19
// -20: 最高优先级
// 0:   默认优先级
// +19: 最低优先级

// 特殊优先级值
#define PRIO_MIN -20
#define PRIO_MAX 19

// 优先级类别
-20 to -1:  高优先级进程
0:          正常优先级进程
+1 to +19:  低优先级进程

10. 实际应用场景 Link to heading

场景1:系统监控工具 Link to heading

int is_high_priority_process(pid_t pid) {
    int priority = safe_getpriority(PRIO_PROCESS, pid);
    return (priority != -999 && priority < 0);
}

场景2:资源管理 Link to heading

void adjust_batch_job_priority() {
    // 批处理作业使用较低优先级
    setpriority(PRIO_PROCESS, 0, 15);
}

场景3:性能优化 Link to heading

void optimize_critical_process() {
    // 关键进程使用较高优先级
    setpriority(PRIO_PROCESS, 0, -5);
}

11. 注意事项 Link to heading

使用 getpriority 时的重要注意事项:

  1. 返回值检查: 必须先清除 errno,因为成功时可能返回 -1
  2. 权限问题: 访问其他进程信息可能需要适当权限
  3. 进程生命周期: 进程结束后相关信息可能不可用
  4. 实时性: 优先级可能在查询期间发生变化
  5. 系统限制: 某些优先级值可能需要特殊权限才能设置

12. 优先级与调度策略 Link to heading

// 不同调度策略的优先级处理
void explain_scheduling_policies() {
    printf("Linux 调度策略优先级说明:\n");
    printf("1. SCHED_FIFO: 实时优先级 1-99\n");
    printf("2. SCHED_RR:   实时优先级 1-99\n");
    printf("3. SCHED_OTHER: 静态优先级 -20 到 +19\n");
    printf("4. SCHED_BATCH: 批处理优化\n");
    printf("5. SCHED_IDLE:  空闲任务\n");
}

总结 Link to heading

getpriority 是进程优先级管理的重要函数,关键要点:

  1. 优先级查询: 获取进程、进程组或用户的调度优先级
  2. 错误处理: 特殊的返回值处理机制(需要清除 errno)
  3. 权限控制: 访问其他进程信息需要适当权限
  4. 系统管理: 在系统监控和资源管理中广泛使用
  5. 性能优化: 帮助识别和调整关键进程的优先级

正确使用 getpriority 可以帮助程序了解当前的调度状态,实现更精细的性能管理和资源控制。