Linux 的进程调度策略和进程优先级是操作系统为保证系统响应性、公平性和高性能所设计的关键机制。
进程调度策略
Linux 支持 两大类调度策略:
- 普通调度策略(CFS: Completely Fair Scheduler), 适用于大部分用户态进程。
- 实时调度策略(Real-Time Scheduling Policies),** **使用在时间要求严格的任务(如工业控制、音频处理)中。
普通调度策略(非实时)
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_NORMAL</font>**
(即**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_OTHER</font>**
)- 默认策略,用于普通进程(非实时进程)。
- 使用 完全公平调度器(CFS, Completely Fair Schedule) 动态分配 CPU 时间,基于进程的
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">nice</font>**
值调整权重,把处理器时间公平地分给每个进程。 - 示例:大多数用户进程(如浏览器、文本编辑器)。
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_IDLE</font>**
- 优先级最低,仅在系统空闲时运行(如后台维护任务)。
实时调度策略
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_FIFO</font>**
(First In Fisrst Out)- 先进先出,先调度先运行。
- 无时间片限制,进程会一直运行直到主动让出 CPU 或更高优先级进程就绪。
- 优先级范围:
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">1</font>**
(最低)到**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">99</font>**
(最高)。
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_RR</font>**
(Round Robin)- 类似
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_FIFO</font>**
,但每个进程分配固定时间片,进程用完时间片以后加入优先级对应运行队列的尾部,把处理器让给优先级相同的其它实时进程。 - 优先级范围同
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_FIFO</font>**
(1-99)。
- 类似
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_DEADLINE</font>**
- 基于任务的截止时间(Deadline)调度,适合实时性要求严格的任务。
- 使用三个参数:运行时间
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">runtime</font>**
、截至期限**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">deadline</font>**
、周期**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">period</font>**
。每个周期运行一次,在截至期限之前执行完,一次运行的时间长度是<font style="color:rgb(64, 64, 64);">runtime</font>
。 - 内核需要开启 CONFIG_SCHED_DEADLINE 。
调度策略定义如下:
/*
* Scheduling policies
*/
#define SCHED_NORMAL 0
#define SCHED_FIFO 1
#define SCHED_RR 2
#define SCHED_BATCH 3
/* SCHED_ISO: reserved but not implemented yet */
#define SCHED_IDLE 5
#define SCHED_DEADLINE 6
进程优先级
- 限期进程的优先级比实时进程要高,实时进程的优先级比普通通进程要高。
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_DEADLINE</font>**
>**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_FIFO</font>**
/**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_RR</font>**
>**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_NORMAL</font>**
。- 限期进程的优先级是-1。
- 实时进程的实时优先级是
<font style="color:rgb(64, 64, 64);">1~99</font>
,优先级数值越大,表示优先级越高。 - 普通进程的静态优先级是
<font style="color:rgb(64, 64, 64);">100~139</font>
,优先级数值越小,表示优先级越高。可通过修改nice值(即相对优先级,取值范围是<font style="color:rgb(64, 64, 64);">-20~19</font>
)改变普通进程的优先级,优先级等于<font style="color:rgb(64, 64, 64);">120</font>
加上nice值。
进程描述符<font style="color:rgb(64, 64, 64);">task_struct</font>
中,有4个成员和优先级相关:
struct task_struct {
...
int prio;
int static_prio;
int normal_prio;
unsigned int rt_priority;
...
};
相关解释如下表:
优先级 | 限期进程 | 实时进程 | 普通进程 |
---|---|---|---|
prio 动态优先级(数值越小,表示优先级越高) | 大多数情况下prio等于normal_prio 特殊情况:优先级继承。如果进程a占有实时互斥锁,进程b正在等待锁,进程b的优先级比进程a的优先级高,那么把进程a的优先级临时提高到进程b的优先级,即进程a的prio值等于进程b的prio值。 | ||
static_prio 静态优先级 | 不适用,总是0 | 不适用,总是0 | 120+nice 值数值越小,表示优先级越高。 初始化后通常不变。 |
normal_prio 进程的“标准化”优先级(数值越小,表示优先级越高),由调度类根据 **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">static_prio</font>** 或 **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">rt_priority</font>** 计算。 | -1(MAX_DL_PRIO-1) | 99(MAX_RT_PRIO - 1 )- rt_priority | static_prio |
rt_priority 实时优先级 | 不适用,总是0 | 实时进程的优先级,范围是1~99,数值越大,表示优先级越高。 仅对实时进程有效。 | 不适用,总是0 |
调度策略 | **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">static_prio</font>** | **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">rt_priority</font>** | **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">normal_prio</font>** | **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">prio</font>** |
---|---|---|---|---|
SCHED_DEADLINE | **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">0</font>** | **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">0</font>** | **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">-1</font>** (特殊值) | 优先级继承场景外,通常等于normal_prio |
SCHED_FIFO/RR | **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">0</font>** | **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">1-99</font>** | **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">100 - 1 - rt_priority</font>** | |
SCHED_NORMAL | **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">100 + nice + 20</font>** | **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">0</font>** | **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">= static_prio</font>** |
调度器如何使用优先级字段
prio
:调度器根据该字段在红黑树中排序(普通任务)或链表(实时任务)中比较。static_prio
:用于计算普通进程的normal_prio
。normal_prio
:- 在新建进程或任务状态切换时用于初始化
prio
- 在进程唤醒后
prio
会从normal_prio
恢复。
- 在新建进程或任务状态切换时用于初始化
rt_priority
:用于计算实时进程的normal_prio
。
内核normal_prio
计算
normal_prio
计算的核心代码如下:
/*
* __normal_prio - return the priority that is based on the static prio
*/
static inline int __normal_prio(struct task_struct *p)
{
return p->static_prio;
}
/*
* Calculate the expected normal priority: i.e. priority
* without taking RT-inheritance into account. Might be
* boosted by interactivity modifiers. Changes upon fork,
* setprio syscalls, and whenever the interactivity
* estimator recalculates.
*/
static inline int normal_prio(struct task_struct *p)
{
int prio;
if (task_has_dl_policy(p)) // SCHED_DEADLINE 任务
prio = MAX_DL_PRIO-1;
else if (task_has_rt_policy(p)) // 实时任务
prio = MAX_RT_PRIO-1 - p->rt_priority;
else // 普通任务
prio = __normal_prio(p);
return prio;
}
调度策略及优先级查询方法
测试代码如下
// gcc -o policy_prio_test policy_prio_test.c -pthread
// ※一定要在root下运行 ./policy_prio_test
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/resource.h> // for setpriority()
volatile sig_atomic_t keep_running = 1;
void signal_handler(int sig) {
if (sig == SIGINT) {
keep_running = 0;
}
}
// 设置线程属性(调度策略和优先级)
void set_thread_attr(pthread_attr_t *attr, int policy, int priority) {
// 初始化线程属性
pthread_attr_init(attr);
// 设置调度策略(SCHED_FIFO/SCHED_RR/SCHED_OTHER)
pthread_attr_setschedpolicy(attr, policy);
// 设置优先级(仅对实时策略有效)
if (policy == SCHED_FIFO || policy == SCHED_RR) {
struct sched_param param;
param.sched_priority = priority;
pthread_attr_setschedparam(attr, ¶m);
}
// 明确指定使用自定义调度参数(不继承父线程)
pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);
}
// 普通线程函数(通过nice值调整优先级)
void *normal_thread_func(void *arg) {
int nice_val = *(int *)arg;
printf("Normal Thread (nice=%d) started. PID=%d, TID=%ld\n",
nice_val, getpid(), (long)pthread_self());
// 设置当前线程的nice值
if (setpriority(PRIO_PROCESS, 0, nice_val) == -1) {
perror("setpriority failed");
}
while (keep_running) {
sleep(1); // 模拟工作
}
printf("Normal Thread (nice=%d) exiting.\n", nice_val);
return NULL;
}
// 实时线程函数(优先级由属性设置)
void *rt_thread_func(void *arg) {
printf("RT Thread started. PID=%d, TID=%ld\n",
getpid(), (long)pthread_self());
while (keep_running) {
sleep(1); // 模拟工作
}
printf("RT Thread exiting.\n");
return NULL;
}
int main() {
signal(SIGINT, signal_handler);
printf("Main process PID: %d\n", getpid());
pthread_t threads[7];
pthread_attr_t attrs[7];
// 线程配置:类型 + 优先级/nice值
struct {
int policy;
int priority; // 对实时线程有效
int nice; // 对普通线程有效
} thread_configs[7] = {
{SCHED_OTHER, 0, 10}, // 普通线程1 (nice=10)
{SCHED_OTHER, 0, 0}, // 普通线程2 (nice=0)
{SCHED_OTHER, 0, -5}, // 普通线程3 (nice=-5)
{SCHED_FIFO, 80, 0}, // FIFO线程1 (优先级80)
{SCHED_FIFO, 90, 0}, // FIFO线程2 (优先级90)
{SCHED_RR, 70, 0}, // RR线程1 (优先级70)
{SCHED_RR, 60, 0} // RR线程2 (优先级60)
};
// 创建所有线程
for (int i = 0; i < 7; i++) {
// 设置线程属性
set_thread_attr(&attrs[i], thread_configs[i].policy, thread_configs[i].priority);
// 创建线程
int ret;
if (thread_configs[i].policy == SCHED_OTHER) {
ret = pthread_create(&threads[i], &attrs[i], normal_thread_func, &thread_configs[i].nice);
} else {
ret = pthread_create(&threads[i], &attrs[i], rt_thread_func, NULL);
}
if (ret != 0) {
fprintf(stderr, "Failed to create thread %d: %s\n", i, strerror(ret));
exit(EXIT_FAILURE);
}
}
// 等待Ctrl+C信号
while (keep_running) {
pause();
}
// 清理线程
printf("Shutting down...\n");
for (int i = 0; i < 7; i++) {
pthread_cancel(threads[i]);
pthread_join(threads[i], NULL);
pthread_attr_destroy(&attrs[i]);
}
return 0;
}
ps
命令查询
# ps -p 44353 -Lo tid,policy,pri,nice,rtprio,cmd
TID POL PRI NI RTPRIO CMD
44353 TS 19 0 - ./policy_prio_test
44354 TS 9 10 - ./policy_prio_test
44355 TS 19 0 - ./policy_prio_test
44356 TS 24 -5 - ./policy_prio_test
44357 FF 120 - 80 ./policy_prio_test
44358 FF 130 - 90 ./policy_prio_test
44359 RR 110 - 70 ./policy_prio_test
44360 RR 100 - 60 ./policy_prio_test
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">pri</font>**
列的值是经过转换的(<font style="color:rgb(64, 64, 64);">139 - prio</font>
),非内核的**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">prio</font>**
原始值。**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">pri</font>**
值越大优先级越高。
top
命令查询
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
44353 root 20 0 60020 620 536 S 0.0 0.0 0:00.00 policy_prio_tes
44354 root 30 10 60020 620 536 S 0.0 0.0 0:00.02 policy_prio_tes
44355 root 20 0 60020 620 536 S 0.0 0.0 0:00.02 policy_prio_tes
44356 root 15 -5 60020 620 536 S 0.0 0.0 0:00.01 policy_prio_tes
44357 root -81 0 60020 620 536 S 0.0 0.0 0:00.02 policy_prio_tes
44358 root -91 0 60020 620 536 S 0.0 0.0 0:00.02 policy_prio_tes
44359 root -71 0 60020 620 536 S 0.0 0.0 0:00.02 policy_prio_tes
44360 root -61 0 60020 620 536 S 0.0 0.0 0:00.02 policy_prio_tes
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">PR</font>**
列(优先级)是**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">ps</font>**
中**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">pri</font>**
的另一种表示方式,非内核原始值。**<font style="color:rgb(64, 64, 64);">PR</font>**
值越小优先级越高。
<font style="color:rgb(64, 64, 64);">chrt</font>
命令查询
chrt
主要用来查询实时进程的调度策略和优先级。查询到的优先级是task_truct
中的rt_priority
,对于普通进程,该值为0。
# chrt -p 44353
pid 44353's current scheduling policy: SCHED_OTHER
pid 44353's current scheduling priority: 0 // 普通进程,rt_priority值为0
# chrt -p 44354
pid 44354's current scheduling policy: SCHED_OTHER
pid 44354's current scheduling priority: 0
# chrt -p 44355
pid 44355's current scheduling policy: SCHED_OTHER
pid 44355's current scheduling priority: 0
# chrt -p 44356
pid 44356's current scheduling policy: SCHED_OTHER
pid 44356's current scheduling priority: 0
# chrt -p 44357
pid 44357's current scheduling policy: SCHED_FIFO
pid 44357's current scheduling priority: 80
# chrt -p 44358
pid 44358's current scheduling policy: SCHED_FIFO
pid 44358's current scheduling priority: 90
# chrt -p 44359
pid 44359's current scheduling policy: SCHED_RR
pid 44359's current scheduling priority: 70
# chrt -p 44360
pid 44360's current scheduling policy: SCHED_RR
pid 44360's current scheduling priority: 60
/proc/<pid>/sched
查询
/proc/<pid>/sched
查询,查询到的值是task_struct
中的prio
。
# cat /proc/44353/sched
policy_prio_tes (44353, #threads: 8)
-------------------------------------------------------------------
se.exec_start : 19758958.662391
se.vruntime : 159031.419195
se.sum_exec_runtime : 0.509459
se.nr_migrations : 0
nr_switches : 8
nr_voluntary_switches : 3
nr_involuntary_switches : 5
se.load.weight : 1048576
se.avg.load_sum : 46917
se.avg.runnable_sum : 24281477
se.avg.util_sum : 24076911
se.avg.load_avg : 1023
se.avg.runnable_avg : 515
se.avg.util_avg : 513
se.avg.last_update_time : 19758958661632
se.avg.util_est.ewma : 513
se.avg.util_est.enqueued : 513
uclamp.min : 0
uclamp.max : 1024
effective uclamp.min : 0
effective uclamp.max : 1024
policy : 0 // SCHED_NORMAL
prio : 120
clock-delta : 14
mm->numa_scan_seq : 0
numa_pages_migrated : 0
numa_preferred_nid : -1
total_numa_faults : 0
current_node=0, numa_group_id=0
numa_faults node=0 task_private=0 task_shared=0 group_private=0 group_shared=0
# cat /proc/44358/sched
policy_prio_tes (44358, #threads: 8)
-------------------------------------------------------------------
se.exec_start : 27409364.575804
se.vruntime : 8.999994
se.sum_exec_runtime : 107.209942
se.nr_migrations : 0
nr_switches : 7652
nr_voluntary_switches : 7652
nr_involuntary_switches : 0
se.load.weight : 1048576
se.avg.load_sum : 47055
se.avg.runnable_sum : 11860176
se.avg.util_sum : 11857104
se.avg.load_avg : 1024
se.avg.runnable_avg : 252
se.avg.util_avg : 252
se.avg.last_update_time : 19758958593024
se.avg.util_est.ewma : 0
se.avg.util_est.enqueued : 0
uclamp.min : 1024
uclamp.max : 1024
effective uclamp.min : 1024
effective uclamp.max : 1024
policy : 1 // SCHED_FIFO
prio : 9
clock-delta : 21
mm->numa_scan_seq : 0
numa_pages_migrated : 0
numa_preferred_nid : -1
total_numa_faults : 0
current_node=0, numa_group_id=0
numa_faults node=0 task_private=0 task_shared=0 group_private=0 group_shared=0
参考资料
- Professional Linux Kernel Architecture,Wolfgang Mauerer
- Linux内核深度解析,余华兵
- Linux设备驱动开发详解,宋宝华
- linux kernel 4.12
※※※下面内容暂时先保留,后续删除
系统调用
每个进程都有一个内核栈
当运行应用程序的时候,调用fork()/vfork()/clone()函数就是系统调用;系统调用就是应用程序如何进入内核空间执行任务,程序使用系统调用执行一系列的操作:如何创建进程、文件IO等;
内核线程
内核线程是直接由内核本身启动的进程;它是独立运行在内核空间的进程,与普通用户进程区别在于内核线程没有独立的进程地址空间;task_struct结构里面mm指针设置为NULL,它只能运行在内核空间。
退出进程
进程主动终止:从main()函数返回,链接程序会自动添加到exit()系统调用;主动调用exit()系统函数;
进程被动终止:进程收到自己不能处理的信号,如SIGKILL等;
- 什么是限期进程,实时进程,普通进程?
- 内核调度策略,SCHED_BATCH,怎么个批量处理;SCHED_IDLE,使task以最低优先级选择CFS调度器来调度运行,这个又是什么意思?
- 调度器类、调度策略,是怎么关联起来的?
- SCHED_OTHER, 与SCHED_NORMAL、SCHED_BATCH、SCHED_IDLE是什么关系?
不应该是数值越小,优先级越高吗?
就这么个意思
是什么呢,从什么呢,然后什么呢,对不对
SMT,超线程
查看进程状态,包括绑核信息
cat /proc/pid/status
查看进程sleep时,kernel当前运行的函数
cat /proc/pid/wchan