Serenity 系统调用深入解析:scheduler_set_parameters 与 scheduler_get_parameters 进程/线程优先级参数读写
发布时间:2026/9/10 5:32:09 锦皓数字建站

Serenity 系统调用深入解析scheduler_set_parameters 与 scheduler_get_parameters 进程/线程优先级参数读写【免费下载链接】serenityThe Serenity Operating System 项目地址: https://gitcode.com/GitHub_Trending/se/serenity本文围绕 Serenity 操作系统的 man page scheduler_get_parameters.md 展开完整覆盖scheduler_set_parameters/scheduler_get_parameters两个系统调用的参数结构、取值范围、权限模型与错误语义并结合 内核调度实现 与 就绪队列调度器 源码讲清优先级参数如何映射到实际的 CPU 调度行为。读完后你可以独立理解这两个系统调用的完整调用链、安全限制以及调度优先级在 Serenity 内核中的真实作用机制。概述两个系统调用解决什么问题scheduler_set_parameters和scheduler_get_parameters是 Serenity 内核中用于修改或读取进程/线程调度参数的系统调用。按 man page 的说法scheduler_set_parameters的修改要在目标进程或线程下一次被调度时才生效因此效果可能不会立即可观察。这对系统调用是对 POSIXsched_setparam/sched_getparam的泛化替代旧的 POSIX 系统调用只是库函数的镜像、通用性差新系统调用则通过一个统一的参数结构体同时覆盖进程级与线程级两种作用域。两个系统调用在内核中注册于 Syscall.h均标记为NeedsBigProcessLock::No——它们不持有进程大锁而是通过调度器锁g_scheduler_lock保护共享状态。参数结构SC_scheduler_parameters_params两个系统调用共享同一个参数结构定义在 Syscall.henum class SchedulerParametersMode : bool { Process, Thread, }; struct SC_scheduler_parameters_params { pid_t pid_or_tid; SchedulerParametersMode mode; struct sched_param parameters; };各字段语义与 man page 一致字段类型含义pid_or_tidpid_t目标进程或线程标识具体解释由mode决定。取值0有特义Process模式下表示当前进程自身Thread模式下表示当前线程自身modeSchedulerParametersMode枚举取值为Process操作整个进程的调度参数或Thread操作线程级调度参数parametersstruct sched_param要读写的调度参数即 POSIXsched_setparam等接口使用的数据结构其中sched_param在 Kernel/API/POSIX/sched.h 中定义struct sched_param { int sched_priority; };目前唯一可用的调度参数就是int sched_priority调度优先级没有sched_policy等其他成员。优先级取值范围同样在 sched.h 中给出了完整取值体系#define THREAD_PRIORITY_MIN 1 #define THREAD_PRIORITY_LOW 10 #define THREAD_PRIORITY_NORMAL 30 #define THREAD_PRIORITY_HIGH 50 #define THREAD_PRIORITY_MAX 99合法区间为1最高 99最低数字越小优先级越高线程默认优先级为THREAD_PRIORITY_NORMAL30见 Thread.h 中u32 m_priority { THREAD_PRIORITY_NORMAL };scheduler_set_parameters中传入区间外的值会被拒绝并返回EINVAL见 sched.cppif (parameters.parameters.sched_priority THREAD_PRIORITY_MIN || parameters.parameters.sched_priority THREAD_PRIORITY_MAX) return EINVAL;实现走读从系统调用入口到优先级落地写参数sys$scheduler_set_parameters完整实现见 sched.cpp执行顺序为权限前置检查TRY(require_promise(Pledge::proc))——两个系统调用都要求调用方持有procpromise对应 man page Security 一节拷入参数copy_typed_from_user把用户态结构体复制到内核栈上参数合法性优先级超出[THREAD_PRIORITY_MIN, THREAD_PRIORITY_MAX]返回EINVAL锁定调度器获取g_scheduler_lock自旋锁随后解析目标线程权限校验非 superuser 时要求调用方凭据的euid或uid与目标进程凭据的uid匹配否则返回EPERMif (!credentials-is_superuser() credentials-euid() ! peer_credentials-uid() credentials-uid() ! peer_credentials-uid()) return EPERM;写优先级peer-set_priority(...)更新目标线程优先级set_priority即 Thread.h 中的void set_priority(u32 p) { m_priority p; }进程级语义覆盖若mode Process则对该进程所有线程逐一写入同一优先级。源码注释明确说明POSIX 规定进程调度参数优先于线程调度参数而 Serenity 并不分别追踪二者因此直接手动覆盖所有线程的线程级设置。读参数sys$scheduler_get_parameters实现见 sched.cpp流程对称要求procpromise →copy_from_user读入pid_or_tid和mode→ 加锁解析目标线程并做同样的 EPERM 校验 → 读取peer-priority()→ 写回parameters.parameters.sched_priority→copy_to_user返回用户空间。注意读取路径不检查优先级范围读到的值来自内核自身状态必然合法。目标解析get_thread_from_pid_or_tid两个系统调用共用的目标解析逻辑在 sched.cpp其关键规则Thread模式pid_or_tid 0时目标为当前线程否则通过Thread::from_tid_in_same_process_list(tid)查找。查找时有一条硬限制——非 superuser 只能访问自己所在进程内的线程跨进程访问线程直接返回EPERMProcess模式pid_or_tid 0时目标为当前进程否则通过Process::from_pid_in_same_process_list(pid)查找找不到返回ESRCH。进程级参数落在主线程上tid pid的线程进程优先级实际通过主线程的优先级读取/写入两种模式下解析结果为空最终都归一为ESRCH。安全模型proc promise 与三条授权规则man page Security 一节规定两个系统调用都要求procpromise源码中对应require_promise(Pledge::proc)未持有时会返回 pledge 相关错误superuser可以修改任何进程或线程的调度参数任意线程可以修改自己所在进程的全部线程以及进程本身的调度参数任意进程可以修改同一用户euid 与 uid 均需与目标进程一致拥有的所有进程的调度参数但不能修改这些进程中具体某个线程的参数即线程级操作仅限本进程内或 root。上面第 2、3 条在源码中有清晰对应进程级/跨进程检查依赖凭据uid比较见上文 EPERM 代码而线程级跨进程限制则在get_thread_from_pid_or_tid的Thread分支中提前拦截。返回值与错误码返回值0表示成功非零为错误对scheduler_get_parameters成功时读取到的优先级写入sched_param子结构后回传用户空间。错误码触发条件源码依据EINVALsched_priority不在 199 范围内仅 set 路径sched.cpp#L67-L68EPERM调用方无权访问目标进程/线程的调度参数sched.cpp#L75-L76、#L33-L34ESRCHpid_or_tid不存在或解析不到目标man page 原文写作ESRC系笔误内核实际返回ESRCHsched.cpp#L43-L44EFAULT参数结构体指针无效copy_typed_from_user/copy_from_user/copy_to_user失败时sched.cpp#L65底层原理优先级如何驱动就绪队列调度优先级不只是存储字段它直接决定线程进入哪个就绪队列桶。核心映射函数thread_priority_to_priority_index在 Scheduler.cppstatic inline u32 thread_priority_to_priority_index(u32 thread_priority) { // Converts the priority in the range of THREAD_PRIORITY_MIN...THREAD_PRIORITY_MAX // to a index into g_ready_queues where 0 is the highest priority bucket constexpr u32 thread_priority_count THREAD_PRIORITY_MAX - THREAD_PRIORITY_MIN 1; auto priority_bucket ((thread_priority_count - (thread_priority - THREAD_PRIORITY_MIN)) / thread_priority_count) * (ThreadReadyQueues::count - 1); return priority_bucket; }从源码结构看Serenity 的就绪队列组织为ThreadReadyQueuesScheduler.cpp一个 32 位mask加上 32 个ThreadReadyQueue每个是IntrusiveList把 99 档用户优先级线性压缩到 32 个桶索引 0 为最高优先级桶调度取线程时pull_next_runnable_thread()Scheduler.cpp从mask高位扫描优先弹出非空的高优先级桶并在桶内跳过不满足 CPU 亲和性affinity的线程无任何就绪线程时回落到该 CPU 的 idle 线程idle 线程被显式设为THREAD_PRIORITY_MINScheduler.cpp#L408保证它永远排在最后。因此一次scheduler_set_parameters调用把线程优先级从 30 调到 10 的实际效果是该线程下次入队时进入更高优先级桶从而在 CPU 竞争时先于普通优先级线程被pull_next_runnable_thread选中——这也解释了 man page 中修改在下次调度时生效的表述。历史背景按 man page History 一节scheduler_set_parameters与scheduler_get_parameters取代了通用性较差的sched_setparam/sched_getparam系统调用后者只是对 POSIX 库函数的精确镜像。新接口用单一结构体同时承载进程级与线程级两种作用域是 Serenity 系统调用层对 POSIX 接口的泛化改造。man page 末尾的 FIXME 注释表明Scheduler(7)手册页尚不存在目前关于调度器的文档以本 man page 和内核源码为主要依据。小结参数结构SC_scheduler_parameters_params以pid_or_tid mode sched_param三元组统一了进程/线程两种调度参数操作优先级合法区间 199THREAD_PRIORITY_MINTHREAD_PRIORITY_MAX默认 30THREAD_PRIORITY_NORMAL越界即EINVAL权限上要求procpromise且非 root 用户只能操作本进程线程或同 uid 进程的进程级参数优先级通过 32 桶就绪队列映射真正影响调度顺序修改在目标线程下次调度时生效。【免费下载链接】serenityThe Serenity Operating System 项目地址: https://gitcode.com/GitHub_Trending/se/serenity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。