Linux 调度器函数sched_*系统调用及示例
Linux 调度器函数详解
1. 概述
sched_*
函数族是 Linux 系统中用于进程调度控制的一系列系统调用。可以把调度器想象成"CPU 时间片的分配管理员"——它决定哪个进程什么时候获得 CPU 时间,就像交通警察决定哪辆车什么时候可以通过路口一样。
这些函数提供了对进程调度策略、优先级、CPU 亲和性等方面的精细控制,是实现高性能、实时应用的重要工具。
2. sched_* 函数列表
2.1 基础调度函数
- sched_yield: 让出当前 CPU 时间片
- sched_getscheduler: 获取进程调度策略
- sched_setscheduler: 设置进程调度策略
- sched_getparam: 获取进程调度参数
- sched_setparam: 设置进程调度参数
2.2 CPU 亲和性函数
- sched_getaffinity: 获取进程 CPU 亲和性
- sched_setaffinity: 设置进程 CPU 亲和性
2.3 优先级函数
- sched_get_priority_min: 获取指定策略的最小优先级
- sched_get_priority_max: 获取指定策略的最大优先级
- sched_rr_get_interval: 获取轮转调度的时间片间隔
2.4 CPU 信息函数
- sched_getcpu: 获取当前 CPU 编号
3. 调度策略详解
3.1 调度策略类型
策略 | 值 | 说明 |
---|---|---|
SCHED_OTHER | 0 | 默认分时调度策略(CFS) |
SCHED_FIFO | 1 | 先进先出实时调度策略 |
SCHED_RR | 2 | 轮转实时调度策略 |
SCHED_BATCH | 3 | 批处理调度策略 |
SCHED_IDLE | 5 | 空闲调度策略 |
SCHED_DEADLINE | 6 | 截止时间调度策略 |
3.2 调度策略特点
#include <sched.h>
#include <stdio.h>void show_scheduling_policies() {printf("=== 调度策略特点 ===\n");printf("SCHED_OTHER (默认):\n");printf(" • 用于普通进程\n");printf(" • 完全公平调度器 (CFS)\n");printf(" • 动态优先级调整\n");printf(" • 适合交互式应用\n\n");printf("SCHED_FIFO (实时 FIFO):\n");printf(" • 实时调度策略\n");printf(" • 高优先级进程一直运行直到阻塞或主动让出\n");printf(" • 不会时间片轮转\n");printf(" • 需要 root 权限\n\n");printf("SCHED_RR (实时轮转):\n");printf(" • 实时调度策略\n");printf(" • 时间片轮转调度\n");printf(" • 相同优先级进程轮流执行\n");printf(" • 需要 root 权限\n\n");printf("SCHED_BATCH (批处理):\n");printf(" • 用于批处理任务\n");printf(" • 减少唤醒频率\n");printf(" • 适合长时间运行的非交互任务\n\n");printf("SCHED_IDLE (空闲):\n");printf(" • 用于极低优先级任务\n");printf(" • 只在系统空闲时运行\n");printf(" • 不影响其他进程\n\n");printf("SCHED_DEADLINE (截止时间):\n");printf(" • 基于截止时间的调度\n");printf(" • 确保任务按时完成\n");printf(" • 需要特殊配置\n");printf(" • Linux 3.14+\n\n");
}
4. 函数详细介绍
4.1 sched_yield - 让出 CPU 时间片
函数原型
#include <sched.h>
int sched_yield(void);
功能
让出当前进程的 CPU 时间片,允许其他同优先级的进程运行。
参数
无参数
返回值
- 成功: 返回 0
- 失败: 返回 -1(实际上很少失败)
示例代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
#include <time.h>
#include <errno.h>// 消耗 CPU 的函数
void consume_cpu(int seconds) {time_t start = time(NULL);volatile long sum = 0;while (time(NULL) - start < seconds) {for (long i = 0; i < 1000000; i++) {sum += i;}}
}int main() {printf("=== sched_yield 示例 ===\n\n");// 获取当前进程 ID 和 CPU 信息printf("进程 ID: %d\n", getpid());printf("父进程 ID: %d\n", getppid());// 获取当前 CPU 编号(如果有支持)int current_cpu = sched_getcpu();if (current_cpu != -1) {printf("当前 CPU: %d\n", current_cpu);} else {printf("无法获取当前 CPU 信息\n");}printf("\n1. 不使用 sched_yield 的 CPU 消耗:\n");printf(" 开始消耗 CPU 时间...\n");time_t start_time = time(NULL);consume_cpu(3); // 消耗 3 秒 CPU 时间time_t end_time = time(NULL);printf(" 消耗完成,用时 %ld 秒\n", end_time - start_time);printf("\n2. 使用 sched_yield 的 CPU 消耗:\n");printf(" 开始消耗 CPU 时间并让出时间片...\n");start_time = time(NULL);time_t yield_start = time(NULL);while (time(NULL) - yield_start < 3) {// 消耗一些 CPU 时间volatile long sum = 0;for (long i = 0; i < 500000; i++) {sum += i;}// 让出 CPU 时间片if (sched_yield() == -1) {perror("sched_yield 失败");}}end_time = time(NULL);printf(" 消耗完成,用时 %ld 秒\n", end_time - start_time);printf("\n3. sched_yield 的实际效果:\n");printf(" • 允许其他同优先级进程运行\n");printf(" • 改善系统响应性\n");printf(" • 减少饥饿现象\n");printf(" • 适合协作式多任务\n");printf("\n=== sched_yield 使用场景 ===\n");printf("1. 长时间运行的循环\n");printf("2. 忙等待循环\n");printf("3. 协作式多任务\n");printf("4. 实时应用中的主动让出\n");printf("5. 负载均衡\n");printf("\n=== 注意事项 ===\n");printf("1. 不保证立即切换到其他进程\n");printf("2. 只影响同优先级进程\n");printf("3. 过度使用可能影响性能\n");printf("4. 不是强制调度切换\n");printf("5. 应该谨慎使用\n");return 0;
}
4.2 sched_getscheduler/sched_setscheduler - 调度策略控制
函数原型
#include <sched.h>int sched_getscheduler(pid_t pid);
int sched_setscheduler(pid_t pid, int policy, const struct sched_param *param);
sched_param 结构体
struct sched_param {int sched_priority; // 调度优先级// 对于 SCHED_DEADLINE,还有额外字段
};
功能
- sched_getscheduler: 获取指定进程的调度策略
- sched_setscheduler: 设置指定进程的调度策略和参数
参数
- pid: 进程 ID(0 表示当前进程)
- policy: 调度策略
- param: 指向调度参数的指针
返回值
- sched_getscheduler: 成功返回调度策略,失败返回 -1
- sched_setscheduler: 成功返回 0,失败返回 -1
示例代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
#include <errno.h>
#include <string.h>
#include <sys/resource.h>// 将调度策略转换为字符串
const char* policy_to_string(int policy) {switch (policy) {case SCHED_OTHER: return "SCHED_OTHER (默认)";case SCHED_FIFO: return "SCHED_FIFO (实时FIFO)";case SCHED_RR: return "SCHED_RR (实时轮转)";case SCHED_BATCH: return "SCHED_BATCH (批处理)";case SCHED_IDLE: return "SCHED_IDLE (空闲)";case SCHED_DEADLINE: return "SCHED_DEADLINE (截止时间)";default: return "未知策略";}
}// 显示进程调度信息
void show_process_scheduling_info(pid_t pid, const char *description) {printf("=== %s ===\n", description);if (pid == 0) {printf("进程: 当前进程 (PID: %d)\n", getpid());} else {printf("进程: PID %d\n", pid);}// 获取调度策略int policy = sched_getscheduler(pid);if (policy == -1) {perror("获取调度策略失败");return;}printf("调度策略: %s\n", policy_to_string(policy));// 获取调度参数struct sched_param param;if (sched_getparam(pid, ¶m) == 0) {printf("调度优先级: %d\n", param.sched_priority);// 显示优先级范围int min_priority = sched_get_priority_min(policy);int max_priority = sched_get_priority_max(policy);if (min_priority != -1 && max_priority != -1) {printf("优先级范围: %d - %d\n", min_priority, max_priority);if (param.sched_priority < min_priority || param.sched_priority > max_priority) {printf("⚠ 当前优先级超出范围\n");}}} else {perror("获取调度参数失败");}// 获取进程优先级errno = 0;int nice_value = getpriority(PRIO_PROCESS, pid);if (errno == 0) {printf("Nice 值: %d\n", nice_value);}printf("\n");
}// 设置调度策略
int set_process_scheduling_policy(pid_t pid, int policy, int priority) {struct sched_param param;param.sched_priority = priority;printf("设置进程调度策略:\n");printf(" 进程 ID: %d\n", pid ? pid : getpid());printf(" 调度策略: %s\n", policy_to_string(policy));printf(" 调度优先级: %d\n", priority);if (sched_setscheduler(pid, policy, ¶m) == 0) {printf("✓ 调度策略设置成功\n");return 0;} else {switch (errno) {case EPERM:printf("✗ 权限不足: 需要 root 权限设置实时策略\n");break;case EINVAL:printf("✗ 参数无效: 策略或优先级无效\n");break;case ESRCH:printf("✗ 进程不存在\n");break;default:printf("✗ 设置失败: %s\n", strerror(errno));break;}return -1;}
}int main() {printf("=== sched_getscheduler/sched_setscheduler 示例 ===\n\n");// 显示当前用户信息printf("用户信息:\n");printf(" UID: %d\n", getuid());printf(" EUID: %d\n", geteuid());printf(" GID: %d\n", getgid());printf(" EGID: %d\n", getegid());printf("\n");// 显示初始调度信息show_process_scheduling_info(0, "初始调度信息");// 显示各种调度策略的优先级范围printf("=== 各种调度策略的优先级范围 ===\n");int policies[] = {SCHED_OTHER, SCHED_FIFO, SCHED_RR, SCHED_BATCH, SCHED_IDLE};int num_policies = sizeof(policies) / sizeof(policies[0]);for (int i = 0; i < num_policies; i++) {int min_priority = sched_get_priority_min(policies[i]);int max_priority = sched_get_priority_max(policies[i]);printf("%-20s: 最小优先级 %3d, 最大优先级 %3d\n", policy_to_string(policies[i]), min_priority, max_priority);}printf("\n");// 演示调度策略设置(需要 root 权限)printf("=== 调度策略设置演示 ===\n");// 1. 尝试设置 SCHED_FIFO(需要 root 权限)printf("1. 尝试设置 SCHED_FIFO 策略:\n");if (set_process_scheduling_policy(0, SCHED_FIFO, 10) == 0) {show_process_scheduling_info(0, "设置 SCHED_FIFO 后");} else {printf(" 说明: SCHED_FIFO 需要 root 权限\n");}// 2. 尝试设置 SCHED_RR(需要 root 权限)printf("\n2. 尝试设置 SCHED_RR 策略:\n");if (set_process_scheduling_policy(0, SCHED_RR, 15) == 0) {show_process_scheduling_info(0, "设置 SCHED_RR 后");} else {printf(" 说明: SCHED_RR 需要 root 权限\n");}// 3. 尝试设置 SCHED_BATCHprintf("\n3. 尝试设置 SCHED_BATCH 策略:\n");if (set_process_scheduling_policy(0, SCHED_BATCH, 0) == 0) {show_process_scheduling_info(0, "设置 SCHED_BATCH 后");} else {printf(" 说明: 可能需要适当权限\n");}// 4. 恢复默认策略printf("\n4. 恢复默认 SCHED_OTHER 策略:\n");struct sched_param default_param = {0};if (sched_setscheduler(0, SCHED_OTHER, &default_param) == 0) {printf("✓ 成功恢复默认调度策略\n");show_process_scheduling_info(0, "恢复默认策略后");} else {printf("✗ 恢复默认策略失败: %s\n", strerror(errno));}printf("\n=== 调度策略使用建议 ===\n");printf("选择原则:\n");printf("1. 普通应用: 使用 SCHED_OTHER(默认)\n");printf("2. 实时应用: 使用 SCHED_FIFO 或 SCHED_RR\n");printf("3. 批处理任务: 使用 SCHED_BATCH\n");printf("4. 后台任务: 使用 SCHED_IDLE\n");printf("5. 截止时间任务: 使用 SCHED_DEADLINE\n");printf("\n");printf("权限要求:\n");printf("1. SCHED_OTHER/SCHED_BATCH/SCHED_IDLE: 普通权限\n");printf("2. SCHED_FIFO/SCHED_RR: 需要 root 权限\n");printf("3. SCHED_DEADLINE: 需要特殊配置\n");printf("\n");printf("性能影响:\n");printf("1. 实时策略: 更高优先级,更低延迟\n");printf("2. 批处理策略: 更低唤醒频率,更好吞吐\n");printf("3. 空闲策略: 不影响其他进程\n");printf("4. 默认策略: 平衡性能和公平性\n");return 0;
}
4.3 sched_getaffinity/sched_setaffinity - CPU 亲和性控制
函数原型
#define _GNU_SOURCE
#include <sched.h>int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);
int sched_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t *mask);
CPU 集合操作宏
void CPU_ZERO(cpu_set_t *set); // 清空 CPU 集合
void CPU_SET(int cpu, cpu_set_t *set); // 设置 CPU
void CPU_CLR(int cpu, cpu_set_t *set); // 清除 CPU
int CPU_ISSET(int cpu, cpu_set_t *set); // 检查 CPU 是否设置
int CPU_COUNT(cpu_set_t *set); // 计算设置的 CPU 数量
功能
- sched_getaffinity: 获取进程的 CPU 亲和性掩码
- sched_setaffinity: 设置进程的 CPU 亲和性掩码
参数
- pid: 进程 ID(0 表示当前进程)
- cpusetsize: CPU 集合的大小
- mask: 指向 CPU 集合的指针
返回值
- 成功: 返回 0
- 失败: 返回 -1,并设置相应的 errno
示例代码
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
#include <errno.h>
#include <string.h>// 显示 CPU 集合信息
void show_cpu_set_info(const cpu_set_t *cpu_set, const char *description) {printf("=== %s ===\n", description);int cpu_count = CPU_COUNT((cpu_set_t*)cpu_set);printf("CPU 数量: %d\n", cpu_count);printf("CPU 列表: ");int printed = 0;for (int i = 0; i < CPU_SETSIZE; i++) {if (CPU_ISSET(i, (cpu_set_t*)cpu_set)) {if (printed > 0) printf(", ");printf("%d", i);printed++;}}if (printed == 0) {printf("无");}printf("\n\n");
}// 获取系统 CPU 信息
void show_system_cpu_info() {printf("=== 系统 CPU 信息 ===\n");// 获取在线 CPU 数量int online_cpus = sysconf(_SC_NPROCESSORS_ONLN);printf("在线 CPU 数量: %d\n", online_cpus);// 获取配置的 CPU 数量int conf_cpus = sysconf(_SC_NPROCESSORS_CONF);printf("配置 CPU 数量: %d\n", conf_cpus);// 显示当前进程的 CPU 亲和性cpu_set_t current_set;CPU_ZERO(¤t_set);if (sched_getaffinity(0, sizeof(current_set), ¤t_set) == 0) {show_cpu_set_info(¤t_set, "当前进程 CPU 亲和性");} else {perror("获取 CPU 亲和性失败");}
}// 设置 CPU 亲和性
int set_process_cpu_affinity(pid_t pid, const int *cpu_list, int cpu_count) {cpu_set_t cpu_set;CPU_ZERO(&cpu_set);printf("设置 CPU 亲和性:\n");printf(" 进程 ID: %d\n", pid ? pid : getpid());printf(" CPU 列表: ");for (int i = 0; i < cpu_count; i++) {if (i > 0) printf(", ");printf("%d", cpu_list[i]);CPU_SET(cpu_list[i], &cpu_set);}printf("\n");if (sched_setaffinity(pid, sizeof(cpu_set), &cpu_set) == 0) {printf("✓ CPU 亲和性设置成功\n");return 0;} else {printf("✗ CPU 亲和性设置失败: %s\n", strerror(errno));return -1;}
}// 创建 CPU 绑定的测试线程
void* cpu_bound_worker(void *arg) {int worker_id = *(int*)arg;printf("工作线程 %d 启动\n", worker_id);// 获取线程的 CPU 信息int current_cpu = sched_getcpu();if (current_cpu != -1) {printf(" 工作线程 %d 运行在 CPU %d 上\n", worker_id, current_cpu);}// 执行一些 CPU 密集型任务volatile long sum = 0;for (long i = 0; i < 10000000; i++) {sum += i;// 偶尔检查 CPU 变化if (i % 1000000 == 0) {int new_cpu = sched_getcpu();if (new_cpu != current_cpu && new_cpu != -1) {printf(" 工作线程 %d 从 CPU %d 切换到 CPU %d\n", worker_id, current_cpu, new_cpu);current_cpu = new_cpu;}}}printf("工作线程 %d 完成\n", worker_id);return NULL;
}int main() {printf("=== sched_getaffinity/sched_setaffinity 示例 ===\n\n");// 显示系统信息show_system_cpu_info();// 1. 获取当前进程的 CPU 亲和性printf("1. 获取当前进程 CPU 亲和性:\n");cpu_set_t initial_set;CPU_ZERO(&initial_set);if (sched_getaffinity(0, sizeof(initial_set), &initial_set) == 0) {show_cpu_set_info(&initial_set, "初始 CPU 亲和性");} else {perror("获取初始 CPU 亲和性失败");}// 2. 设置 CPU 亲和性(绑定到特定 CPU)printf("2. 设置 CPU 亲和性:\n");// 获取系统 CPU 数量int online_cpus = sysconf(_SC_NPROCESSORS_ONLN);printf("系统在线 CPU 数量: %d\n", online_cpus);if (online_cpus > 1) {// 绑定到第一个 CPUint single_cpu[] = {0};if (set_process_cpu_affinity(0, single_cpu, 1) == 0) {cpu_set_t new_set;CPU_ZERO(&new_set);if (sched_getaffinity(0, sizeof(new_set), &new_set) == 0) {show_cpu_set_info(&new_set, "设置后 CPU 亲和性");}}// 绑定到前两个 CPUprintf("绑定到前两个 CPU:\n");int two_cpus[] = {0, 1};if (set_process_cpu_affinity(0, two_cpus, 2) == 0) {cpu_set_t new_set;CPU_ZERO(&new_set);if (sched_getaffinity(0, sizeof(new_set), &new_set) == 0) {show_cpu_set_info(&new_set, "绑定到前两个 CPU 后");}}} else {printf("系统只有一个 CPU,跳过 CPU 绑定测试\n");}// 3. 恢复初始 CPU 亲和性printf("3. 恢复初始 CPU 亲和性:\n");if (sched_setaffinity(0, sizeof(initial_set), &initial_set) == 0) {printf("✓ 成功恢复初始 CPU 亲和性\n");cpu_set_t restored_set;CPU_ZERO(&restored_set);if (sched_getaffinity(0, sizeof(restored_set), &restored_set) == 0) {show_cpu_set_info(&restored_set, "恢复后 CPU 亲和性");}} else {printf("✗ 恢复初始 CPU 亲和性失败: %s\n", strerror(errno));}// 4. 显示 CPU 亲和性的好处printf("=== CPU 亲和性的好处 ===\n");printf("性能优化:\n");printf("1. 减少 CPU 缓存失效\n");printf("2. 提高缓存命中率\n");printf("3. 降低上下文切换开销\n");printf("4. 改善 NUMA 访问模式\n");printf("5. 提高多核应用性能\n");printf("\n");printf("应用场景:\n");printf("1. 高性能计算应用\n");printf("2. 实时系统\n");printf("3. 数据库服务器\n");printf("4. 游戏服务器\n");printf("5. 科学计算\n");printf("6. 音视频处理\n");printf("\n");printf("注意事项:\n");printf("1. 过度限制可能影响负载均衡\n");printf("2. NUMA 架构需要考虑内存亲和性\n");printf("3. 应该根据应用特点合理设置\n");printf("4. 避免与其他进程争抢 CPU\n");printf("5. 监控系统整体性能\n");return 0;
}
4.4 sched_get_priority_min/sched_get_priority_max - 优先级范围查询
函数原型
#include <sched.h>int sched_get_priority_min(int policy);
int sched_get_priority_max(int policy);
功能
获取指定调度策略的最小和最大优先级值。
参数
- policy: 调度策略
返回值
- 成功: 返回对应的优先级值
- 失败: 返回 -1,并设置相应的 errno
示例代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
#include <errno.h>
#include <string.h>// 显示调度策略优先级范围
void show_policy_priority_range(int policy, const char *policy_name) {int min_priority = sched_get_priority_min(policy);int max_priority = sched_get_priority_max(policy);printf("%-20s: ", policy_name);if (min_priority == -1 || max_priority == -1) {printf("不支持 (%s)\n", strerror(errno));} else {printf("最小优先级 %3d, 最大优先级 %3d", min_priority, max_priority);if (min_priority == max_priority) {printf(" (固定优先级)");} else if (min_priority < max_priority) {printf(" (动态优先级范围)");}printf("\n");}
}// 显示所有调度策略的优先级范围
void show_all_policy_ranges() {printf("=== 所有调度策略优先级范围 ===\n");struct {int policy;const char *name;} policies[] = {{SCHED_OTHER, "SCHED_OTHER"},{SCHED_FIFO, "SCHED_FIFO"},{SCHED_RR, "SCHED_RR"},{SCHED_BATCH, "SCHED_BATCH"},{SCHED_IDLE, "SCHED_IDLE"},{SCHED_DEADLINE, "SCHED_DEADLINE"},{-1, NULL}};for (int i = 0; policies[i].name; i++) {show_policy_priority_range(policies[i].policy, policies[i].name);}printf("\n");
}// 优先级验证函数
int validate_priority_for_policy(int policy, int priority) {int min_priority = sched_get_priority_min(policy);int max_priority = sched_get_priority_max(policy);if (min_priority == -1 || max_priority == -1) {return -1; // 策略不支持}if (priority >= min_priority && priority <= max_priority) {return 0; // 优先级有效} else {return 1; // 优先级无效}
}// 显示优先级验证结果
void show_priority_validation(int policy, int priority) {const char *policy_name = NULL;switch (policy) {case SCHED_OTHER: policy_name = "SCHED_OTHER"; break;case SCHED_FIFO: policy_name = "SCHED_FIFO"; break;case SCHED_RR: policy_name = "SCHED_RR"; break;case SCHED_BATCH: policy_name = "SCHED_BATCH"; break;case SCHED_IDLE: policy_name = "SCHED_IDLE"; break;case SCHED_DEADLINE: policy_name = "SCHED_DEADLINE"; break;default: policy_name = "未知策略"; break;}printf("验证 %s 策略优先级 %d: ", policy_name, priority);int result = validate_priority_for_policy(policy, priority);switch (result) {case -1:printf("策略不支持\n");break;case 0:printf("✓ 有效\n");break;case 1:printf("✗ 无效\n");break;}
}int main() {printf("=== sched_get_priority_min/max 示例 ===\n\n");// 显示所有策略的优先级范围show_all_policy_ranges();// 显示当前进程信息printf("当前进程信息:\n");printf(" 进程 ID: %d\n", getpid());printf(" 父进程 ID: %d\n", getppid());printf(" 用户 ID: %d\n", getuid());printf(" 组 ID: %d\n", getgid());// 获取当前进程调度策略int current_policy = sched_getscheduler(0);if (current_policy != -1) {printf(" 当前调度策略: %d ", current_policy);switch (current_policy) {case SCHED_OTHER: printf("(SCHED_OTHER)\n"); break;case SCHED_FIFO: printf("(SCHED_FIFO)\n"); break;case SCHED_RR: printf("(SCHED_RR)\n"); break;case SCHED_BATCH: printf("(SCHED_BATCH)\n"); break;case SCHED_IDLE: printf("(SCHED_IDLE)\n"); break;case SCHED_DEADLINE: printf("(SCHED_DEADLINE)\n"); break;default: printf("(未知)\n"); break;}// 获取当前进程优先级struct sched_param current_param;if (sched_getparam(0, ¤t_param) == 0) {printf(" 当前优先级: %d\n", current_param.sched_priority);// 验证当前优先级show_priority_validation(current_policy, current_param.sched_priority);}} else {perror("获取当前调度策略失败");}printf("\n");// 验证不同策略的优先级printf("=== 优先级验证示例 ===\n");// SCHED_OTHER 策略printf("SCHED_OTHER 策略:\n");show_priority_validation(SCHED_OTHER, 0); // 有效show_priority_validation(SCHED_OTHER, -1); // 无效show_priority_validation(SCHED_OTHER, 10); // 无效// SCHED_FIFO 策略printf("\nSCHED_FIFO 策略:\n");show_priority_validation(SCHED_FIFO, 10); // 可能有效show_priority_validation(SCHED_FIFO, 50); // 可能有效show_priority_validation(SCHED_FIFO, 99); // 可能有效show_priority_validation(SCHED_FIFO, 100); // 可能无效// SCHED_RR 策略printf("\nSCHED_RR 策略:\n");show_priority_validation(SCHED_RR, 10); // 可能有效show_priority_validation(SCHED_RR, 50); // 可能有效show_priority_validation(SCHED_RR, 99); // 可能有效show_priority_validation(SCHED_RR, 100); // 可能无效// SCHED_BATCH 策略printf("\nSCHED_BATCH 策略:\n");show_priority_validation(SCHED_BATCH, 0); // 有效show_priority_validation(SCHED_BATCH, -1); // 无效show_priority_validation(SCHED_BATCH, 10); // 无效// SCHED_IDLE 策略printf("\nSCHED_IDLE 策略:\n");show_priority_validation(SCHED_IDLE, 0); // 有效show_priority_validation(SCHED_IDLE, -1); // 无效show_priority_validation(SCHED_IDLE, 10); // 无效printf("\n=== 优先级使用建议 ===\n");printf("优先级设置原则:\n");printf("1. 了解策略的优先级范围\n");printf("2. 合理分配优先级值\n");printf("3. 避免优先级反转\n");printf("4. 考虑系统整体平衡\n");printf("5. 验证优先级的有效性\n");printf("\n");printf("实时策略优先级:\n");printf("1. SCHED_FIFO/SCHED_RR: 1-99 (通常)\n");printf("2. 数值越大优先级越高\n");printf("3. 需要 root 权限\n");printf("4. 谨慎使用高优先级\n");printf("5. 避免独占 CPU\n");printf("\n");printf("普通策略优先级:\n");printf("1. SCHED_OTHER: 通常为 0\n");printf("2. 通过 nice 值调整\n");printf("3. 使用动态优先级\n");printf("4. 平衡系统负载\n");printf("5. 适合交互式应用\n");return 0;
}
4.5 sched_rr_get_interval - 轮转调度时间片
函数原型
#include <sched.h>int sched_rr_get_interval(pid_t pid, struct timespec *tp);
功能
获取 SCHED_RR 策略的时间片长度。
参数
- pid: 进程 ID(0 表示当前进程)
- tp: 指向 timespec 结构体的指针,用于存储时间片长度
返回值
- 成功: 返回 0
- 失败: 返回 -1,并设置相应的 errno
示例代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
#include <errno.h>
#include <string.h>
#include <time.h>// 显示时间片信息
void show_timeslice_info(const struct timespec *interval, const char *description) {printf("=== %s ===\n", description);if (interval->tv_sec == 0 && interval->tv_nsec == 0) {printf("时间片长度: 未设置\n");} else {printf("时间片长度: %ld.%09ld 秒\n", (long)interval->tv_sec, (long)interval->tv_nsec);printf("时间片长度: %.3f 毫秒\n", (double)interval->tv_sec * 1000 + (double)interval->tv_nsec / 1000000);printf("时间片长度: %.3f 微秒\n", (double)interval->tv_sec * 1000000 + (double)interval->tv_nsec / 1000);printf("时间片长度: %ld 纳秒\n", (long)interval->tv_sec * 1000000000 + (long)interval->tv_nsec);}printf("\n");
}// 获取 RR 调度时间片
int get_rr_timeslice(pid_t pid) {struct timespec interval;printf("获取 SCHED_RR 时间片长度:\n");printf(" 进程 ID: %d\n", pid ? pid : getpid());if (sched_rr_get_interval(pid, &interval) == 0) {show_timeslice_info(&interval, "RR 调度时间片");return 0;} else {switch (errno) {case EINVAL:printf("✗ 进程不是 SCHED_RR 策略\n");break;case ESRCH:printf("✗ 进程不存在\n");break;default:printf("✗ 获取时间片失败: %s\n", strerror(errno));break;}return -1;}
}// 比较不同策略的时间片
void compare_policy_intervals() {printf("=== 不同策略时间片比较 ===\n");// 获取当前进程时间片(假设是 SCHED_OTHER)struct timespec current_interval;if (sched_rr_get_interval(0, ¤t_interval) == 0) {printf("当前进程时间片: %ld.%09ld 秒\n", (long)current_interval.tv_sec, (long)current_interval.tv_nsec);} else {printf("当前进程时间片: 无法获取 (%s)\n", strerror(errno));}// 显示系统时间片配置printf("\n系统时间片配置:\n");// 读取内核参数FILE *fp = fopen("/proc/sys/kernel/sched_rr_timeslice_ms", "r");if (fp) {char buffer[64];if (fgets(buffer, sizeof(buffer), fp)) {printf(" RR 时间片 (ms): %s", buffer);}fclose(fp);} else {printf(" RR 时间片: 无法读取系统配置\n");}// 显示其他相关配置printf("其他相关配置:\n");system("cat /proc/sys/kernel/sched_child_runs_first 2>/dev/null || echo ' 无法读取 sched_child_runs_first'");system("cat /proc/sys/kernel/sched_autogroup_enabled 2>/dev/null || echo ' 无法读取 sched_autogroup_enabled'");printf("\n");
}int main() {printf("=== sched_rr_get_interval 示例 ===\n\n");// 显示系统信息printf("系统信息:\n");printf(" 内核版本: ");system("uname -r | tr -d '\\n'");printf("\n");printf(" CPU 架构: ");system("uname -m | tr -d '\\n'");printf("\n");printf(" 进程 ID: %d\n", getpid());printf(" 父进程 ID: %d\n", getppid());printf("\n");// 1. 获取当前进程时间片printf("1. 获取当前进程时间片:\n");get_rr_timeslice(0);// 2. 尝试获取不存在进程的时间片printf("2. 尝试获取不存在进程的时间片:\n");if (sched_rr_get_interval(999999, &(struct timespec){0}) == -1) {if (errno == ESRCH) {printf("✓ 正确处理不存在进程: ESRCH\n");} else {printf("✗ 意外错误: %s\n", strerror(errno));}}// 3. 尝试获取非 RR 策略进程的时间片printf("3. 尝试获取非 RR 策略进程的时间片:\n");if (sched_rr_get_interval(0, &(struct timespec){0}) == -1) {if (errno == EINVAL) {printf("✓ 正确处理非 RR 策略进程: EINVAL\n");printf(" 说明: 当前进程使用默认 SCHED_OTHER 策略\n");} else {printf("✗ 其他错误: %s\n", strerror(errno));}}// 4. 显示系统时间片配置compare_policy_intervals();// 5. 显示时间片对性能的影响printf("=== 时间片对性能的影响 ===\n");printf("时间片长度的影响:\n");printf("1. 较短时间片:\n");printf(" • 更好的响应性\n");printf(" • 更多上下文切换\n");printf(" • 更低吞吐量\n");printf(" • 更高延迟\n");printf("\n");printf("2. 较长时间片:\n");printf(" • 更低响应性\n");printf(" • 更少上下文切换\n");printf(" • 更高吞吐量\n");printf(" • 更低延迟\n");printf("\n");printf("3. 默认时间片:\n");printf(" • 平衡响应性和吞吐量\n");printf(" • 通常为 10-100ms\n");printf(" • 适应大多数应用\n");printf(" • 可配置调整\n");printf("\n");printf("应用场景:\n");printf("1. 实时系统: 需要短时间片保证响应性\n");printf("2. 批处理系统: 需要长时间片提高吞吐量\n");printf("3. 交互式应用: 需要适中时间片平衡各方面\n");printf("4. 服务器应用: 根据负载动态调整\n");printf("5. 嵌入式系统: 根据硬件特性优化\n");printf("\n");printf("配置建议:\n");printf("1. 监控上下文切换频率\n");printf("2. 调整时间片适应应用特点\n");printf("3. 考虑系统整体负载\n");printf("4. 测试不同配置的性能\n");printf("5. 避免极端配置影响系统稳定性\n");return 0;
}
4.6 sched_getcpu - 获取当前 CPU 编号
函数原型
#define _GNU_SOURCE
#include <sched.h>int sched_getcpu(void);
功能
获取调用线程当前运行的 CPU 编号。
参数
无参数
返回值
- 成功: 返回 CPU 编号(从 0 开始)
- 失败: 返回 -1,并设置相应的 errno
示例代码
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <sys/syscall.h>// 获取 CPU 信息的辅助函数
void show_cpu_info() {printf("=== CPU 信息 ===\n");// 获取当前 CPU 编号int current_cpu = sched_getcpu();if (current_cpu != -1) {printf("当前 CPU 编号: %d\n", current_cpu);} else {printf("无法获取当前 CPU 编号: %s\n", strerror(errno));}// 获取系统 CPU 数量long online_cpus = sysconf(_SC_NPROCESSORS_ONLN);long conf_cpus = sysconf(_SC_NPROCESSORS_CONF);printf("在线 CPU 数量: %ld\n", online_cpus);printf("配置 CPU 数量: %ld\n", conf_cpus);// 显示 CPU 信息printf("CPU 详细信息:\n");system("lscpu | head -10 2>/dev/null || echo '无法获取 CPU 详细信息'");printf("\n");
}// 线程函数,显示线程的 CPU 信息
void* thread_cpu_info(void* arg) {int thread_id = *(int*)arg;int initial_cpu, current_cpu;// 获取初始 CPUinitial_cpu = sched_getcpu();printf("线程 %d:\n", thread_id);printf(" 初始 CPU: %d\n", initial_cpu);// 执行一些工作并检查 CPU 变化volatile long sum = 0;for (long i = 0; i < 10000000; i++) {sum += i;// 偶尔检查 CPU 变化if (i % 1000000 == 0) {current_cpu = sched_getcpu();if (current_cpu != initial_cpu && current_cpu != -1) {printf(" 线程 %d 从 CPU %d 切换到 CPU %d\n", thread_id, initial_cpu, current_cpu);initial_cpu = current_cpu;}}}// 最终 CPUcurrent_cpu = sched_getcpu();printf(" 最终 CPU: %d\n", current_cpu);printf(" 线程 %d 完成\n", thread_id);return NULL;
}// CPU 绑定测试
void test_cpu_binding() {printf("=== CPU 绑定测试 ===\n");// 获取系统 CPU 数量long online_cpus = sysconf(_SC_NPROCESSORS_ONLN);if (online_cpus <= 1) {printf("系统只有一个 CPU,跳过绑定测试\n");return;}// 创建多个线程pthread_t threads[4];int thread_ids[4] = {1, 2, 3, 4};printf("创建 4 个线程进行 CPU 绑定测试:\n");for (int i = 0; i < 4; i++) {if (pthread_create(&threads[i], NULL, thread_cpu_info, &thread_ids[i]) != 0) {perror("创建线程失败");return;}}// 等待所有线程完成for (int i = 0; i < 4; i++) {pthread_join(threads[i], NULL);}printf("CPU 绑定测试完成\n\n");
}int main() {printf("=== sched_getcpu 示例 ===\n\n");// 显示系统信息show_cpu_info();// 显示当前进程信息printf("进程信息:\n");printf(" 进程 ID: %d\n", getpid());printf(" 父进程 ID: %d\n", getppid());printf(" 线程 ID: %ld\n", syscall(SYS_gettid));// 获取当前 CPUint current_cpu = sched_getcpu();if (current_cpu != -1) {printf(" 当前运行 CPU: %d\n", current_cpu);} else {printf(" 无法获取 CPU 信息: %s\n", strerror(errno));}printf("\n");// 连续获取 CPU 信息观察变化printf("连续获取 CPU 信息 (执行密集计算):\n");volatile long sum = 0;int initial_cpu = sched_getcpu();printf(" 初始 CPU: %d\n", initial_cpu);for (long i = 0; i < 50000000; i++) {sum += i;// 每隔一定次数检查 CPUif (i % 10000000 == 0) {int current_cpu = sched_getcpu();if (current_cpu != -1) {printf(" 计算 %ld 次后 CPU: %d\n", i, current_cpu);}}}int final_cpu = sched_getcpu();printf(" 最终 CPU: %d\n", final_cpu);if (initial_cpu != final_cpu && initial_cpu != -1 && final_cpu != -1) {printf(" ✓ CPU 在计算过程中发生了切换\n");} else {printf(" CPU 在计算过程中保持不变\n");}printf("\n");// CPU 绑定测试test_cpu_binding();// 显示 CPU 调度信息printf("=== CPU 调度信息 ===\n");printf("CPU 调度相关概念:\n");printf("1. CPU 亲和性: 进程可以运行的 CPU 集合\n");printf("2. 负载均衡: 系统在 CPU 间分配负载\n");printf("3. 上下文切换: 进程在 CPU 间的切换\n");printf("4. 缓存局部性: 数据在 CPU 缓存中的位置\n");printf("5. NUMA 拓扑: 非统一内存访问架构\n");printf("\n");printf("sched_getcpu 的用途:\n");printf("1. 性能分析: 监控线程 CPU 使用情况\n");printf("2. 负载均衡: 了解 CPU 分配情况\n");printf("3. 调试工具: 分析程序执行行为\n");printf("4. 实时系统: 监控实时性约束\n");printf("5. 优化建议: 识别性能瓶颈\n");printf("\n");printf("使用建议:\n");printf("1. 结合 CPU 亲和性使用\n");printf("2. 监控 CPU 切换频率\n");printf("3. 分析热点 CPU\n");printf("4. 优化缓存局部性\n");printf("5. 避免过度绑定 CPU\n");return 0;
}
3. 编译和运行说明
# 编译所有示例程序
gcc -o pread_example pread_example.c
gcc -o affinity_example affinity_example.c
gcc -o priority_example priority_example.c
gcc -o rr_interval_example rr_interval_example.c
gcc -o getcpu_example getcpu_example.c -lpthread# 运行示例程序
./pread_example
./affinity_example
./priority_example
./rr_interval_example
./getcpu_example# 需要 root 权限的测试
sudo ./priority_example
sudo ./rr_interval_example
4. 系统要求检查
# 检查内核版本
uname -r# 检查系统调用支持
grep -E "(pread|pwrite)" /usr/include/asm/unistd_64.h# 检查 CPU 信息
lscpu# 检查调度器信息
cat /proc/sched_debug 2>/dev/null || echo "无法读取调度器调试信息"# 检查实时调度支持
grep -i realtime /boot/config-$(uname -r)# 检查 NUMA 支持
numactl --hardware 2>/dev/null || echo "系统不支持 NUMA"
5. 重要注意事项
5.1 权限要求
- 普通用户: 可以使用
pread
/pwrite
等基本 I/O 操作 - root 用户: 需要设置实时调度策略 (
SCHED_FIFO
/SCHED_RR
) - CAP_SYS_NICE: 某些操作需要此能力
5.2 错误处理
// 安全的系统调用封装
int safe_pread(int fd, void *buf, size_t count, off_t offset) {if (fd < 0 || !buf || count == 0) {errno = EINVAL;return -1;}ssize_t result;do {result = pread(fd, buf, count, offset);} while (result == -1 && errno == EINTR);return result;
}// 安全的 CPU 亲和性设置
int safe_sched_setaffinity(pid_t pid, const cpu_set_t *mask) {if (!mask) {errno = EINVAL;return -1;}return sched_setaffinity(pid, sizeof(cpu_set_t), mask);
}
5.3 性能考虑
// 性能优化建议
void performance_optimization_tips() {printf("性能优化建议:\n");printf("1. 批量操作: 使用 preadv/pwritev 减少系统调用次数\n");printf("2. 缓冲区大小: 合理设置缓冲区大小避免频繁调用\n");printf("3. CPU 亲和性: 合理绑定 CPU 提高缓存命中率\n");printf("4. 调度策略: 根据应用特点选择合适的调度策略\n");printf("5. 优先级设置: 避免过度使用高优先级影响系统稳定性\n");printf("6. 时间片调整: 根据应用需求调整时间片长度\n");printf("7. 资源限制: 合理设置进程资源限制\n");printf("8. 内存管理: 避免内存碎片和频繁分配\n");
}
5.4 最佳实践
// 完整的 I/O 操作最佳实践
typedef struct {int fd;size_t buffer_size;int use_positioned_io;int use_scatter_gather;cpu_set_t cpu_affinity;int has_cpu_affinity;
} io_context_t;// 初始化 I/O 上下文
int init_io_context(io_context_t *ctx, const char *filename) {ctx->fd = open(filename, O_RDWR | O_CREAT, 0644);if (ctx->fd == -1) {return -1;}ctx->buffer_size = 4096;ctx->use_positioned_io = 1;ctx->use_scatter_gather = 0;ctx->has_cpu_affinity = 0;return 0;
}// 清理 I/O 上下文
void cleanup_io_context(io_context_t *ctx) {if (ctx->fd != -1) {close(ctx->fd);ctx->fd = -1;}
}
6. 实际应用场景
6.1 数据库系统
// 数据库页 I/O 操作
int db_page_io(int fd, off_t page_offset, void *page_data, size_t page_size) {// 使用 pread/pwrite 进行页级别的随机访问ssize_t bytes_read = pread(fd, page_data, page_size, page_offset);return (bytes_read == (ssize_t)page_size) ? 0 : -1;
}
6.2 实时系统
// 实时应用调度设置
int setup_realtime_scheduling(int priority) {struct sched_param param;param.sched_priority = priority;// 设置实时调度策略if (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {return -1;}// 设置 CPU 亲和性cpu_set_t cpu_set;CPU_ZERO(&cpu_set);CPU_SET(0, &cpu_set); // 绑定到 CPU 0return sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
}
6.3 网络服务器
// 网络服务器 I/O 处理
int handle_network_io(int client_fd, struct iovec *iov, int iovcnt) {// 使用 preadv/pwritev 处理网络数据包return pwritev(client_fd, iov, iovcnt, 0);
}
7. 总结
7.1 核心概念回顾
Linux 调度器函数族为进程调度控制提供了精细化的管理能力:
- sched_yield: 让出当前 CPU 时间片,允许同优先级进程运行
- sched_getscheduler/sched_setscheduler: 获取和设置进程调度策略
- sched_getaffinity/sched_setaffinity: 获取和设置 CPU 亲和性
- sched_get_priority_min/sched_get_priority_max: 获取调度策略优先级范围
- sched_rr_get_interval: 获取轮转调度时间片长度
- sched_getcpu: 获取当前运行的 CPU 编号
- prlimit64: 获取和设置进程资源限制
7.2 调度策略详解
五种主要调度策略:
- SCHED_OTHER: 默认分时调度(CFS),适合普通应用
- SCHED_FIFO: 实时先进先出,高优先级进程持续运行
- SCHED_RR: 实时轮转调度,相同优先级进程时间片轮转
- SCHED_BATCH: 批处理优化,减少上下文切换
- SCHED_IDLE: 空闲任务,只在系统空闲时运行
7.3 性能优化要点
调度策略选择:
- 普通应用:使用 SCHED_OTHER(默认)
- 实时系统:使用 SCHED_FIFO 或 SCHED_RR
- 批处理任务:使用 SCHED_BATCH
- 后台任务:使用 SCHED_IDLE
CPU 亲和性优化:
- 减少 CPU 缓存失效
- 提高缓存命中率
- 降低上下文切换开销
- 改善 NUMA 访问模式
7.4 安全和权限管理
权限要求:
- 普通用户:可使用 SCHED_OTHER/SCHED_BATCH/SCHED_IDLE
- root 用户:可使用所有调度策略
- CAP_SYS_NICE:允许修改调度策略和优先级
- CAP_SYS_ADMIN:允许使用 prlimit64 设置资源限制
安全考虑:
// 权限检查示例
int check_scheduling_permissions() {if (geteuid() == 0) {return 1; // root 权限}// 检查 CAP_SYS_NICE 能力// 使用 libcap-ng 库进行能力检查return 0; // 普通权限
}
7.5 实际应用场景
适用场景:
- 实时系统:音视频处理、工业控制(SCHED_FIFO/SCHED_RR)
- 高性能计算:科学计算、数据分析(CPU 亲和性绑定)
- 服务器应用:Web 服务、数据库(合理的调度策略)
- 系统监控:性能分析、资源管理(sched_getcpu)
- 容器技术:资源限制、进程隔离(prlimit64)
7.6 最佳实践
调度策略设置:
// 安全的调度策略设置
int safe_set_scheduler(pid_t pid, int policy, int priority) {struct sched_param param;// 验证参数if (priority < sched_get_priority_min(policy) || priority > sched_get_priority_max(policy)) {errno = EINVAL;return -1;}param.sched_priority = priority;// 设置调度策略int result = sched_setscheduler(pid, policy, ¶m);if (result == -1) {switch (errno) {case EPERM:fprintf(stderr, "权限不足,需要适当权限\n");break;case EINVAL:fprintf(stderr, "无效的策略或优先级\n");break;case ESRCH:fprintf(stderr, "进程不存在\n");break;}}return result;
}
CPU 亲和性管理:
// 智能 CPU 绑定
int smart_cpu_binding(pid_t pid, int cpu_count) {cpu_set_t cpu_set;CPU_ZERO(&cpu_set);// 根据系统 CPU 数量智能绑定int available_cpus = sysconf(_SC_NPROCESSORS_ONLN);int bind_count = (cpu_count < available_cpus) ? cpu_count : available_cpus;for (int i = 0; i < bind_count; i++) {CPU_SET(i, &cpu_set);}return sched_setaffinity(pid, sizeof(cpu_set), &cpu_set);
}
资源限制控制:
// 安全的资源限制设置
int safe_set_resource_limit(int resource, rlim_t soft_limit, rlim_t hard_limit) {struct rlimit64 limit;limit.rlim_cur = soft_limit;limit.rlim_max = hard_limit;int result = prlimit64(0, resource, &limit, NULL);if (result == -1) {switch (errno) {case EPERM:fprintf(stderr, "权限不足,需要 CAP_SYS_RESOURCE 能力\n");break;case EINVAL:fprintf(stderr, "无效的资源类型或限制值\n");break;}}return result;
}
7.7 学习建议
掌握路径:
1. 入门阶段:理解基本调度概念和 sched_yield 使用
2. 进阶阶段:掌握调度策略和 CPU 亲和性
3. 高级阶段:精通资源限制和性能优化
4. 专家阶段:实现复杂的调度控制系统
实践要点:
- 从简单示例开始逐步复杂化
- 重点关注权限管理和错误处理
- 实际项目中验证调度效果
- 持续关注实时系统发展
这些调度器函数是 Linux 系统编程的重要组成部分,正确掌握和使用它们对于开发高性能、实时性要求高的应用程序至关重要。通过系统的学习和实践,开发者可以充分发挥 Linux 调度系统的强大功能。