如何在C中实现Round-Robin调度并避免系统调度干扰?
如何用自定义Round-Robin调度完全替代系统调度器管理进程?
你的核心问题在于:仅用SIGSTOP/SIGCONT无法完全屏蔽系统调度器的干预,原因有两点:一是调度线程优先级不足,可能被目标进程抢占导致切换不及时;二是目标进程默认使用SCHED_OTHER调度策略,系统会根据进程优先级自动调度,即使手动暂停/唤醒,也可能出现不符合预期的调度行为。
要完全接管进程调度,你需要从以下几个方面调整:
关键调整点
- 重构进程层级:让调度器所在进程作为两个目标进程的父进程,确保对目标进程的控制权限更直接,同时能正确处理子进程状态变化。
- 初始暂停所有目标进程:创建子进程后立即发送
SIGSTOP,让它们处于暂停状态,完全由调度器决定何时唤醒,避免系统调度器自主启动它们。 - 提升调度器线程优先级:将调度器线程设置为
SCHED_FIFO调度策略并赋予高优先级,确保它能在时间片到期时及时抢占当前运行的目标进程,保证切换时机准确。 - 修改目标进程的调度策略:将目标进程设置为
SCHED_FIFO,这样它们被唤醒后会持续运行直到被SIGSTOP暂停,不会被系统调度器主动抢占(除非有更高优先级的进程,比如你的调度器线程)。 - 使用精确的计时机制:替换轮询式的时间检查为
timer_create+信号的方式,既节省CPU资源,又能保证时间片到期时触发切换的准确性。
修改后的示例代码
#include <stdlib.h> #include <pthread.h> #include <time.h> #include <unistd.h> #include <sys/time.h> #include <stdio.h> #include <signal.h> #include <sys/wait.h> #include <stdbool.h> #include <sched.h> #define TIME_QUANTUM_MS 2000 // 时间片2秒 pid_t pids[2]; bool current_running = 0; // 当前运行的进程索引(0或1) timer_t switch_timer; // 定时器触发的切换函数 void switch_process(union sigval arg) { // 暂停当前进程 kill(pids[current_running], SIGSTOP); printf("[调度器] 暂停进程 %d\n", pids[current_running]); // 切换到另一个进程 current_running = !current_running; kill(pids[current_running], SIGCONT); printf("[调度器] 恢复进程 %d\n", pids[current_running]); // 重启定时器 struct itimerspec ts = { .it_interval = {0, TIME_QUANTUM_MS * 1000000}, // 重复间隔 .it_value = {0, TIME_QUANTUM_MS * 1000000} // 下次触发时间 }; timer_settime(switch_timer, 0, &ts, NULL); } // 目标进程1的任务 void process1() { while(1) { printf("进程P1正在运行\n"); usleep(500000); // 模拟工作负载 } } // 目标进程2的任务 void process2() { while(1) { printf("进程P2正在运行\n"); usleep(500000); // 模拟工作负载 } } // 初始化调度器定时器 void init_scheduler_timer() { struct sigevent sev = { .sigev_notify = SIGEV_THREAD, .sigev_value.sival_ptr = &switch_timer, .sigev_notify_function = switch_process, .sigev_notify_attributes = NULL }; // 创建定时器 timer_create(CLOCK_MONOTONIC, &sev, &switch_timer); // 设置初始触发时间 struct itimerspec ts = { .it_interval = {0, TIME_QUANTUM_MS * 1000000}, .it_value = {0, TIME_QUANTUM_MS * 1000000} }; timer_settime(switch_timer, 0, &ts, NULL); } // 设置线程为高优先级SCHED_FIFO void set_high_priority(pthread_t thread) { struct sched_param param; param.sched_priority = sched_get_priority_max(SCHED_FIFO); pthread_setschedparam(thread, SCHED_FIFO, ¶m); } int main(int argc, char *argv[]) { // 创建进程P1 pids[0] = fork(); if (pids[0] == 0) { // 设置P1为SCHED_FIFO调度策略 struct sched_param param = {.sched_priority = 10}; sched_setscheduler(0, SCHED_FIFO, ¶m); process1(); exit(0); } // 创建进程P2 pids[1] = fork(); if (pids[1] == 0) { // 设置P2为SCHED_FIFO调度策略 struct sched_param param = {.sched_priority = 10}; sched_setscheduler(0, SCHED_FIFO, ¶m); process2(); exit(0); } // 初始暂停两个进程 kill(pids[0], SIGSTOP); kill(pids[1], SIGSTOP); printf("[调度器] 初始暂停所有进程\n"); // 创建调度器线程 pthread_t scheduler_thread; pthread_create(&scheduler_thread, NULL, (void *)init_scheduler_timer, NULL); // 提升调度器线程优先级 set_high_priority(scheduler_thread); // 唤醒第一个进程 kill(pids[current_running], SIGCONT); printf("[调度器] 启动第一个进程 %d\n", pids[current_running]); // 等待子进程结束(实际不会结束,只是防止父进程退出) while (wait(NULL) > 0); return 0; }
运行注意事项
- 需要以root权限运行程序,因为修改进程调度策略和优先级需要
CAP_SYS_NICE权限。 - 目标进程的
SCHED_FIFO优先级要低于调度器线程的优先级(示例中调度器用最高优先级,目标进程用10),确保调度器能及时抢占。 - 示例中用
CLOCK_MONOTONIC时钟,避免系统时间变化影响计时准确性。
内容的提问来源于stack exchange,提问作者Jgl
相关产品推荐
相关产品推荐

