主要参考:Linux manual page - sched
概述
自从linux内核2.6.23以来,默认的进程调度器就被设置为完全公平调度器(CFS,complete fair scheduler),取代了之前的O(1)调度器。
每个线程都有一个静态调度优先级,即sched_priority
字段。
一个线程的调度策略决定了线程会被插入到同级静态优先级的线程队列的位置,以及它在队列中会怎样移动。
所有的调度都是可插入的,如果一个更高静态优先级的线程准备好了,现在运行中的线程就会被插入。而调度策略则仅仅影响了同样静态优先级的线程。
设置方法
进程(线程)可以通过系统调用设置自身或者其他进程(线程)的调度策略。
- 设置调度策略和参数:
#include <sched.h>
int sched_setattr(pid_t pid, struct sched_attr *attr,
unsigned int flags);
int sched_getattr(pid_t pid, struct sched_attr *attr,
unsigned int size, unsigned int flags);
其中pid
为0时,设置自身的调度策略和参数。结构体sched_attr
包含以下字段:size
、sched_policy
(即调度策略,具体会在下一节介绍)、sched_flags
、sched_nice
、sched_runtime
、sched_deadline
、sched_period
(最后三个为SCHED_DEADLINE
相关的参数)。当设置成功,系统调用返回0;否则返回-1,并会设置errno
。
- 设置CPU亲和性
int sched_setaffinity(pid_t pid, size_t cpusetsize,
cpu_set_t mask);
int sched_getaffinity(pid_t pid, size_t cpusetsize,
cpu_set_t mask);
调度策略
普通进程:SCHED_OTHER
/SCHED_BATCH
/SCHED_IDLE
实时进程:SCHED_FIFO
/SCHED_RR
特殊实时进程:SCHED_DEADLINE
静态优先级:Static_priority:对于普通进程,静态优先级为0;对于实时进程,静态优先级为1-99,99为最高优先级。
动态优先级:Dynamic_priority:仅对普通进程有用,取决于nice和一个动态调整的量(比如进程ready却没被调度,则增加)。
- SCHED_FIFO:
FIFO指的是First In First Out。选择SCHED_FIFO策略的进程不会分时间片,一旦开始调度,就调度至结束,除非被其他更高优先级进程插入。一次调度完成后,回到该优先级队列的末尾。如果当前被调度的进程优先级低于新增的FIFO进程,该FIFO进程会插入。 - SCHED_RR:
RR是Round Robin的缩写。该调度策略是对FIFO的优化,FIFO调度方式容易导致进程长时间占据CPU而阻塞了其他同样重要的进程的调度。在RR调度策略下,每个进程的每次调度到某一时间长度后,便回到该优先级的调度队列的最后。 - SCHED_DEADLINE:
这种调度策略有几个主要指标:Start_Time,Arrival_Time,Relative_Deadline,Period,Absolute_Deadline,Computation_Time
有三个主要参数:RunTime,Period,RelativeDeadline(均为时间长度),其中RunTime是用户预估的进程每次执行所需要的CPU时间,常常被设置为平均执行时间,对于一些重要进程,也可能被设置为最长执行时间。Period是执行周期,每个周期内,进程会且只会被调度一次。RelativeDeadline则是从进程准备好的时刻算起,必须被处理完的Deadline。
以SCHED_DEADLINE策略进行调度的进程不存在优先级,调度时不会被中断,确定执行。
之前一直有一个疑问:内核如何保证实时进程的实时性?比如如果我们设置了不合理的实时进程分配,内核是否还能保证实时性?实际上,以SCHED_DEADLINE调度为例,在设置的时候,会有可行性检查,如果Kernal认为不可行,会拒绝。 - SCHED_OTHER:
即SCHED_NORMAL,优先级为零,按照CFS规则进行调度。
其他
- 可以通过nice调整进程/线程的niceness,并进而影响普通进程的动态优先级。niceness是Per Thread的。
- cgroups只能影响非实时进程。