第 35 章 进程优先级与调度 (Process Priorities and Scheduling)
核心结论
-
*nice 值范围 –20 (高优先级) 到 19 (低优先级)*,默认 0;用于「间接」影响 CFS 调度器权重;2.6.23 CFS 调度器对 nice 差异更敏感;特权进程可设负值,非特权只能调高(降低优先级)。
-
getpriority/setpriority (PRIO_PROCESS/PRIO_PGRP/PRIO_USER):取/设 nice 值;返回 unice = 20 – knice(避免负返回值);调用前必须 errno = 0(成功可能返回 -1)。
-
POSIX 实时调度策略 SCHED_RR (round-robin) 与 SCHED_FIFO:实时进程严格优先于 SCHED_OTHER;优先级 1-99(Linux);同优先级 SCHED_RR/SCHED_FIFO 等价;高优先级可独占 CPU。
-
Linux 非实时策略 SCHED_BATCH (2.6.16+) 与 SCHED_IDLE (2.6.23+):BATCH 减少频繁唤醒进程的调度频率;IDLE 优先级低于 nice +19。
-
sched_setscheduler/sched_setparam/sched_getscheduler/sched_getparam:改/查调度策略与优先级;非特权受 RLIMIT_RTPRIO 限制;成功调用后进程移到队列尾部。
-
sched_yield() 让出 CPU:同优先级有其他进程 → 移到队尾;无其他 → 不动;非实时进程行为未定义。
-
RLIMIT_RTTIME (2.6.25+) 控制实时进程连续 CPU 时间:单位微秒;超过 → SIGXCPU;SCHED_RESET_ON_FORK (2.6.32+) 防止子继承实时策略。
-
sched_setaffinity/sched_getaffinity (Linux-specific):设置/获取 CPU 亲和性掩码(per-thread);CPU_* 宏操作 cpu_set_t;CPU_SETSIZE = 1024;改善 cache locality。
|
本章主旨
本章深入 Linux 进程调度——从传统的 nice 值到 POSIX 实时调度 API,再到 Linux 特有的 CPU 亲和性。读者应掌握:nice 值的范围与含义、CFS 调度器对 nice 的处理、POSIX 实时调度策略(SCHED_RR/SCHED_FIFO/SCHED_OTHER/SCHED_BATCH/SCHED_IDLE)的差异、sched_setscheduler 等 API、RLIMIT_RTPRIO/RLIMIT_RTTIME 资源限制、SCHED_RESET_ON_FORK 标志、sched_yield 让出 CPU、sched_setaffinity 控制 CPU 亲和性。理解「实时调度可能 lock up 系统」是写实时应用的关键——runaway 实时进程独占 CPU,需要 RLIMIT_CPU/alarm/watchdog/RLIMIT_RTTIME 多重防护。这是写音视频处理、嵌入式控制、工业自动化等实时应用的基础。 |
一、核心概念
本章围绕 6 个核心概念展开:从 nice 值入手,到 POSIX 实时调度策略、sched_setscheduler API、资源限制、sched_yield、CPU 亲和性。
| 概念 | 定义 + 重要性 | 实现提示 |
|---|---|---|
nice 值与 CFS 调度 |
nice 值范围 –20 (高) 到 19 (低);默认 0;CFS 用 nice 作调度权重(2.6.23 强敏感);传统只有特权可设负 nice;Linux 2.6.12+ RLIMIT_NICE 允许非特权设负值(上限 20 - rlim_cur)。 |
§35.1;getpriority/setpriority 取/设(PRIO_PROCESS/PRIO_PGRP/PRIO_USER);getpriority 返回 unice = 20 – knice;调用前必须 errno = 0;nice 命令行 nice(1)/renice(8)。 |
POSIX 实时调度策略 |
SCHED_FIFO(无时间片,独占 CPU 到放弃/被抢占)、SCHED_RR(round-robin,同优先级共享 CPU);Linux 优先级 1-99;SCHED_BATCH(批处理)+ SCHED_IDLE(极低优先级);都优于 SCHED_OTHER。 |
§35.2;sched_get_priority_min/max 查范围;Linux 1-99;同优先级 SCHED_FIFO/SCHED_RR 等价;高优先级进程可独占 CPU——runaway 风险。 |
sched_setscheduler API |
改策略 + 优先级;pid=0 → 调用者;policy 接受 SCHED_RR/FIFO/OTHER/BATCH/IDLE;param.sched_priority;成功移到队列尾;非特权受 RLIMIT_RTPRIO 限制。 |
§35.3.2;sched_setparam 只改优先级;sched_getscheduler/getparam 查;fork 继承、exec 保留;SCHED_RESET_ON_FORK 防子继承实时。 |
实时进程资源限制 |
RLIMIT_CPU(SIGXCPU 杀死)+ alarm(SIGALRM)+ watchdog 进程 + RLIMIT_RTTIME(2.6.25+,单位 μs,连续 CPU 时间不阻塞系统调用则计数);防止 runaway 实时进程 lock 系统。 |
§35.3.2 末;RLIMIT_RTPRIO(非特权设实时优先级上限);realtime scheduling groups (CONFIG_RT_GROUP_SCHED, 2.6.25+)。 |
sched_yield 让出 CPU |
同优先级有其他进程 → 移到队尾;无其他 → 不动;Linux 总成功(SUSv3 允许失败);非实时进程行为未定义。 |
§35.3.3;典型用途——实时进程间协作;无需锁但要让同优先级进程运行。 |
CPU 亲和性 (sched_setaffinity) |
Linux-specific;cpu_set_t 表示 CPU 掩码;CPU_ZERO/SET/CLR/ISSET 宏操作;CPU_SETSIZE = 1024;per-thread 属性;gettid() 改特定线程;改善 cache locality、隔离关键进程。 |
§35.4;sched_getaffinity 查掩码;fork 继承、exec 保留;isolcpus 启动参数隔离 CPU;cpuset 更精细控制(多 NUMA)。 |
二、详细笔记
35.1 nice 值与 CFS 调度
What:nice 值是每个进程的调度权重指标,范围 –20(高优先级)到 +19(低优先级),默认 0;CFS 用作权重。
Why:nice 是最简单的「间接」调度控制手段——非特权进程可降低自己的优先级(提高 nice)以「nice to others」;特权进程可提高优先级(降低 nice)。
How:
// 摘自《The Linux Programming Interface》第 35 章
#include <sys/resource.h>
int getpriority(int which, id_t who); /* 返回 nice 值或 -1 */
int setpriority(int which, id_t who, int prio);
API 要点:
-
which:
PRIO_PROCESS(who=PID)、PRIO_PGRP(who=PGID)、PRIO_USER(who=UID)。 -
who=0 → 调用者 PID/PGID/UID。
-
getpriority 返回
unice = 20 – knice——避免负返回(系统调用错误返回)。 -
调用 getpriority 前必须
errno = 0——成功也可能返回 -1。 -
setpriority 试图超出范围自动 bounding 到 –20/+19。
-
PRIO_PGRP/PRIO_USER 返回「优先级最高的」(最低 nice 值)。
权限(§35.1):
-
特权(CAP_SYS_NICE):可改任意进程的 nice。
-
非特权:可改自己或 euid/ruid 匹配的进程。
-
Linux 2.6.12+ 非特权可提高自己 nice(受 RLIMIT_NICE 限制)——上限
20 – rlim_cur。 -
早期内核:非特权只能单向降低(提高 nice)——「irreversibly lower」。
CFS 行为(§35.1):
-
2.6.23+ CFS 调度器对 nice 差异敏感——低 nice 进程获更多 CPU(之前差异较小)。
-
不会完全 starving——即使是 +19 也能拿到少量 CPU。
-
nice 跨 fork 继承、exec 保留。
When:降低批量作业优先级(nice +19);实时/关键进程提权(需 CAP_SYS_NICE)。
Example(§35.1,Listing 35-1):
// 摘自《The Linux Programming Interface》第 35 章
// 摘自 procpri/t_setpriority.c
which = (argv[1][0] == 'p') ? PRIO_PROCESS :
(argv[1][0] == 'g') ? PRIO_PGRP : PRIO_USER;
who = getLong(argv[2], 0, "who");
prio = getInt(argv[3], 0, "priority");
setpriority(which, who, prio);
errno = 0;
prio = getpriority(which, who); /* 成功可能返回 -1 */
if (prio == -1 && errno != 0) errExit("getpriority");
35.2 POSIX 实时调度
What:POSIX 实时调度 API 提供 SCHED_RR(round-robin)与 SCHED_FIFO(first-in first-out)两种实时策略;总是优先于 SCHED_OTHER。
Why:实时应用需保证最大响应时间——高优先级进程必须能立即抢占 SCHED_OTHER 进程;POSIX 仅「soft realtime」——硬实时需特殊 OS 支持。
How:
策略对比(§35.2):
| 策略 | 行为 | 放弃 CPU 条件 |
|---|---|---|
SCHED_FIFO |
无时间片;独占 CPU |
阻塞系统调用/sched_yield/终止/被更高优先级抢占 |
SCHED_RR |
有时间片;同优先级 round-robin |
时间片到/阻塞/sched_yield/终止/被抢占 |
SCHED_OTHER |
标准 round-robin 时间共享 |
默认 |
SCHED_BATCH (2.6.16+) |
类似 SCHED_OTHER,减少频繁唤醒 |
批处理 |
SCHED_IDLE (2.6.23+) |
极低优先级(低于 nice +19) |
仅其他进程不要 CPU 时才运行 |
Linux 优先级范围:
-
SCHED_RR/SCHED_FIFO:1(最低)到 99(最高)。
-
SCHED_OTHER:只能是 0。
-
同优先级 SCHED_RR/SCHED_FIFO 等价——调度顺序取决于队列位置。
多处理器注意(§35.2):
-
每 CPU 独立 run queue——高优先级进程 A 等 CPU 0 不会抢占低优先级进程 B 在 CPU 1。
-
解——用 sched_setaffinity 把关键进程绑到指定 CPU。
-
典型:4 CPU 系统把 3 个隔离给实时应用,1 个给其他进程。
When:实时控制(工业自动化、媒体播放);低延迟网络服务;音频处理。
Example:见 §35.3.2。
35.3 实时调度 API
What:sched_setscheduler/sched_setparam/sched_getscheduler/sched_getparam + sched_yield/sched_rr_get_interval;改/查调度属性。
Why:精细控制进程的调度策略与优先级——软实时应用必备。
How:
API 速查(§35.3):
// 摘自《The Linux Programming Interface》第 35 章
#include <sched.h>
struct sched_param { int sched_priority; };
int sched_setscheduler(pid_t pid, int policy, const struct sched_param *param);
int sched_setparam(pid_t pid, const struct sched_param *param);
int sched_getscheduler(pid_t pid);
int sched_getparam(pid_t pid, struct sched_param *param);
int sched_get_priority_min(int policy);
int sched_get_priority_max(int policy);
int sched_yield(void);
int sched_rr_get_interval(pid_t pid, struct timespec *tp);
参数与返回值:
-
pid=0 → 调用者进程/线程。
-
policy:
SCHED_RR/SCHED_FIFO/SCHED_OTHER/SCHED_BATCH/SCHED_IDLE。 -
sched_setscheduler 成功返回 0(Linux 偏离 SUSv3——SUSv3 应返回前 policy)。
-
sched_setscheduler/setparam 成功后将进程移到队列尾部。
-
fork 继承调度属性;exec 保留。
权限与 RLIMIT_RTPRIO(§35.3.2):
-
特权(CAP_SYS_NICE):任意改。
-
非特权 + RLIMIT_RTPRIO > 0:可设 realtime policy;上限 = max(当前 realtime priority, RLIMIT_RTPRIO)。
-
非特权 + RLIMIT_RTPRIO == 0:只能降低 realtime priority 或切回非实时。
-
SCHED_IDLE 进程不能改策略(无论 RLIMIT_RTPRIO)。
-
跨用户:caller euid 匹配 target ruid 或 euid。
-
RLIMIT_RTPRIO 不授权改其他进程——只授权自己。
SCHED_RESET_ON_FORK(§35.3.2):
-
2.6.32+ 新增。
-
标志 ORed with policy:
sched_setscheduler(pid, SCHED_FIFO | SCHED_RESET_ON_FORK, &sp)。 -
效果:fork 子进程不会继承实时策略(SCHED_OTHER)+ nice 值重置为 0。
-
用途:媒体播放应用——单一进程实时,避免 fork bomb。
-
启用后只有特权能禁用;子进程的 reset-on-fork 标志自动清除。
sched_yield(§35.3.3):
-
同优先级有其他可运行进程 → 移到队尾,让其他进程运行。
-
无其他 → 不动。
-
Linux 总是成功(SUSv3 允许失败);非实时进程行为未定义。
sched_rr_get_interval(§35.3.4):
-
返回 SCHED_RR 时间片长度到 timespec。
-
Linux 2.6+:0.1 秒(100 ms)。
防止 runaway 实时进程(§35.3.2 末):
-
RLIMIT_CPU(秒)→ SIGXCPU。
-
alarm()(秒)→ SIGALRM。
-
watchdog 进程(高实时优先级)→ 监控其他进程 CPU time + 必要时降权/停止。
-
RLIMIT_RTTIME(2.6.25+,μs)→ 实时进程连续 CPU 时间不阻塞则计数;超过 SIGXCPU。
-
realtime scheduling groups(CONFIG_RT_GROUP_SCHED)→ 进程组级别时间限制。
When:写实时应用;写 CPU 密集型服务需要精确控制优先级;debug 调度问题。
Example(§35.3.2,Listing 35-2):
// 摘自《The Linux Programming Interface》第 35 章 — Listing 35-2
// 摘自 procpri/sched_set.c
pol = (argv[1][0] == 'r') ? SCHED_RR :
(argv[1][0] == 'f') ? SCHED_FIFO :
(argv[1][0] == 'b') ? SCHED_BATCH :
(argv[1][0] == 'i') ? SCHED_IDLE :
SCHED_OTHER;
sp.sched_priority = getInt(argv[2], 0, "priority");
sched_setscheduler(getLong(argv[j], 0, "pid"), pol, &sp);
35.4 CPU 亲和性
What:sched_setaffinity/sched_getaffinity 设置/获取进程(或线程)允许运行的 CPU 子集。
Why:避免跨 CPU cache invalidation;隔离关键进程;同 CPU 共享数据更高效。
How:
// 摘自《The Linux Programming Interface》第 35 章
#define _GNU_SOURCE
#include <sched.h>
int sched_setaffinity(pid_t pid, size_t len, cpu_set_t *set);
int sched_getaffinity(pid_t pid, size_t len, cpu_set_t *set);
void CPU_ZERO(cpu_set_t *set);
void CPU_SET(int cpu, cpu_set_t *set);
void CPU_CLR(int cpu, cpu_set_t *set);
int CPU_ISSET(int cpu, cpu_set_t *set);
要点:
-
cpu_set_t 应视为不透明结构(虽然实现为位掩码)——用宏操作。
-
CPU_SETSIZE = 1024——最大 1024 CPU(编号 0-1023)。
-
len 参数传 sizeof(cpu_set_t)。
-
pid=0 → 调用线程;gettid() → 改特定线程。
-
per-thread 属性——线程组内可独立设置。
-
fork 继承;exec 保留。
-
未设置 → 默认所有 CPU(sched_getaffinity 返回全集合)。
-
权限:caller euid 匹配 target ruid/euid,或特权。
-
set 中 CPU 不存在 → EINVAL。
isolcpus 与 cpuset(§35.4):
-
isolcpus=2,3内核参数:隔离 CPU 2,3——只有 sched_setaffinity 可在那运行。 -
cpuset:更精细控制 CPU + 内存节点分配(多 NUMA、大规模系统)。
-
用 cpuset 实现「第 35.3 节最后场景」比 sched_setaffinity 更高效。
When:实时应用隔离到专用 CPU;NUMA 亲和(同 node);多线程同数据共享绑同 CPU。
Example:
// 摘自《The Linux Programming Interface》第 35 章
cpu_set_t set;
CPU_ZERO(&set);
CPU_SET(1, &set); CPU_SET(2, &set); CPU_SET(3, &set);
sched_setaffinity(pid, sizeof(set), &set); /* 不在 CPU 0 运行 */
三、关键图表
|
Linux 调度策略对比(表 35-1)
实时优先级 1-99;Linux 上 SCHED_RR/SCHED_FIFO 同优先级等价。 |
|
sched_setscheduler 权限与 RLIMIT_RTPRIO(2.6.12+)
|
|
防止 runaway 实时进程的 5 种方法
|
四、思维导图
mindmap
root((第 35 章 调度))
nice 值
范围 20 到 19
默认 0
CFS 权重 2 6 23 强敏感
getpriority setpriority
PRIO PROCESS PGRP USER
unice 20 knice
errno 0 必须
RLIMIT NICE 非特权可设负值
POSIX 实时策略
SCHED FIFO 无时间片
SCHED RR round robin
1 99 Linux
同优先级 FIFO RR 等价
SCHED OTHER 标准
SCHED BATCH 2 6 16
SCHED IDLE 2 6 23
高优先级独占 CPU
sched setscheduler API
setscheduler 改策略 优先级
setparam 仅优先级
getscheduler getparam
成功移到队列尾
fork 继承 exec 保留
RLIMIT RTPRIO 限制
SCHED RESET ON FORK 2 6 32
sched yield
同优先级让出
队尾
Linux 总成功
非实时未定义
资源限制
RLIMIT CPU SIGXCPU
alarm SIGALRM
watchdog 进程
RLIMIT RTTIME 2 6 25
realtime group CONFIG
防 runaway 实时进程
CPU 亲和性
sched setaffinity
sched getaffinity
cpu set t 1024 CPU
CPU ZERO SET CLR ISSET
per thread 属性
gettid 特定线程
fork exec 保留
isolcpus 启动参数
cpuset 精细控制
五、重点与易错点
-
*nice 值范围 –20 到 19*:高 nice = 低优先级;CFS 2.6.23 对 nice 差异更敏感(之前差异小)。
-
getpriority 返回 unice = 20 – knice:避免与错误返回 -1 冲突;调用前必须 errno = 0。
-
非特权只能降低自己的优先级(提高 nice);Linux 2.6.12+ RLIMIT_NICE 允许非特权设负值。
-
POSIX 仅 soft realtime:硬实时需 RTOS 或 PREEMPT_RT Linux patch;Linux 主线 2.6.18+ 加了部分功能。
-
实时进程可能 lock 系统——runaway SCHED_FIFO/SCHED_RR 独占 CPU;要 RLIMIT_CPU + alarm + watchdog + RLIMIT_RTTIME 多重防护。
-
SCHED_FIFO 无时间片——独占 CPU 直到阻塞/sched_yield/终止/被抢占;SCHED_RR 同优先级 round-robin。
-
Linux 上 SCHED_RR/SCHED_FIFO 同优先级等价——调度顺序取决于队列位置。
-
sched_setscheduler Linux 返回 0(成功)——SUSv3 应返回前 policy;可移植代码只检查 != -1。
-
RLIMIT_RTPRIO 限制非特权设 realtime 优先级上限——非特权 RLIMIT_RTPRIO > 0 可设,但不超过 max(当前, limit);RLIMIT_RTPRIO == 0 只能降低。
-
SCHED_IDLE 进程不能改策略——无论 RLIMIT_RTPRIO。
-
SCHED_RESET_ON_FORK (2.6.32+) 防止子继承实时策略——fork 时子切回 SCHED_OTHER + nice=0;用于媒体播放;启用后只有特权能禁用。
-
RLIMIT_RTTIME (2.6.25+) 控制实时进程连续 CPU 时间——单位 μs;阻塞系统调用时计数清零;超过 → SIGXCPU;不防止调度切换清零(preempt 不清零)。
-
sched_setaffinity 是 per-thread 属性——线程组内可独立设置;gettid() 改特定线程;pid=0 改调用线程。
-
cpu_set_t 应视为不透明——用 CPU_ZERO/SET/CLR/ISSET 宏操作;CPU_SETSIZE = 1024。
-
CPU 亲和性 per-thread——fork 继承;exec 保留;isolcpus 内核参数 + cpuset 实现 CPU 隔离。
-
sched_setscheduler 成功移到队列尾——避免「刚设完就抢 CPU」导致不公平;同理 sched_setparam。
-
多 CPU 系统每 CPU 独立 run queue——A 高优先级等 CPU 0 不会抢 B 低优先级在 CPU 1;用 sched_setaffinity 绑关键进程到专用 CPU。
-
sched_yield 非实时进程行为未定义——只对实时进程有意义;同优先级让出,无其他不动。
-
跨章衔接:第 24 章 fork 基础 → 第 25 章进程终止 → 第 35 章调度策略;第 36 章资源限制 RLIMIT_CPU/RLIMIT_RTPRIO/RLIMIT_RTTIME;第 39 章 capabilities CAP_SYS_NICE;第 33 章线程 → per-thread 调度属性 + SCHED_FIFO/RR 线程调度;第 28 章 clone → CLONE_THREAD 共享调度策略。
-