全面解析Linux内核架构:进程调度的深度探讨(第2章)
最编程
2024-08-04 15:26:02
...
补充进程优先级、实时进程和调度策略相关的知识(参考《Linux/Unix系统编程手册》第35章)
-
进程优先级:每个普通进程有一个nice值表示进程的优先级
-20
(高优先级) ~+19
(低优先级),默认为0
- 进程优先级越高,代表进程越容易被内核调度到
- 传统UNIX实现中,只有特权进程才能赋给自己小于0的nice值;非特权级只能赋一个大于等于0的nice值
- 可以通过
setpriority()
和getpriority()
设置和获取进程优先级
-
实时进程:实时应用(比如交通导航系统)要求内核在非常短的时间内响应,因此内核提供了一种调度方式能够抢占所有低优先级的进程
- Linux内核提供了99个实时优先级,数值从
1
(最低) ~99
(最高) - 可以通过
sched_setscheduler()
和sched_getscheduler()
设置和获取实时进程优先级
- Linux内核提供了99个实时优先级,数值从
-
调度策略
-
SCHED_NORMAL
(也称SCHED_OTHER
):用于普通进程,通过完全公平调度器(CFS)处理 -
SCHED_BATCH
:用于非交互、CPU使用密集的批处理进程,通过CFS处理 -
SCHED_IDLE
:用于相对权重最小的进程,通过CFS处理(注意:SCHED_IDLE不负责调度空闲进程,空闲进程由内核提供单独机制处理) -
SCHED_RR
:用于软实时进程,轮询调度,通过实时调度器类处理 -
SCHED_FIFO
:用于软实时进程,先进先出调度,通过实时调度器类处理
-
【例】普通进程:获取和修改进程优先级
/* get_nice.c */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/resource.h>
int main(int argc, char* argv[]) {
int prio;
id_t who;
who = atoi(argv[1]);
prio = getpriority(PRIO_PROCESS, who);
if (prio == -1 && errno != 0) {
printf("get prio error\n");
exit(1);
}
printf("get prio: %d\n", prio);
exit(0);
}
/* set_nice.c */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/resource.h>
int main(int argc, char* argv[]) {
int prio;
id_t who;
who = atoi(argv[1]);
prio = atoi(argv[2]);
if (setpriority(PRIO_PROCESS, who, prio) == -1) {
printf("set prio error\n");
exit(1);
}
printf("set prio: %d\n", prio);
exit(0);
}
【例】实时进程:修改和获取策略和优先级
/* get_sched.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sched.h>
int main(int argc, char* argv[]) {
char policy[10] = {0};
struct sched_param param;
pid_t pid = atoi(argv[1]);
int pol = sched_getscheduler(pid);
if (pol == -1 && errno != 0) {
printf("get scheduler error\n");
exit(1);
}
if (sched_getparam(pid, ¶m) == -1) {
printf("get scheduler error\n");
exit(1);
}
switch (pol) {
case SCHED_OTHER:
strcpy(policy, "NORMAL");
break;
#ifdef SCHED_BATCH
case SCHED_BATCH:
strcpy(policy, "BATCH");
break;
#endif
#ifdef SCHED_IDLE
case SCHED_IDLE:
strcpy(policy, "IDLE");
break;
#endif
case SCHED_RR:
strcpy(policy, "RR");
break;
case SCHED_FIFO:
strcpy(policy, "FIFO");
break;
}
printf("%s, %d\n", policy, param.sched_priority);
exit(0);
}
/* set_sched.c */
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
int main(int argc, char *argv[]) {
struct sched_param param;
pid_t pid = atoi(argv[1]);
int pri = atoi(argv[2]);
// pri = sched_get_priority_max(SCHED_FIFO);
param.sched_priority = pri;
if (sched_setscheduler(pid, SCHED_FIFO, ¶m) == -1) {
perror("sched_setscheduler() failed");
exit(1);
}
printf("FIFO, %d\n", pri);
exit(0);
}
【测试结果】
//创建一个普通进程:默认nice=0,调度策略为NORMAL,实时优先级0
[root@RCD sched]# sleep 100 &
[1] 6799
[root@RCD sched]# ./get_nice 6799
get prio: 0
[root@RCD sched]# ./set_nice 6799 10
set prio: 10
[root@RCD sched]# ./get_nice 6799
get prio: 10
//修改为实时进程,调度策略变为FIFO,实时优先级99
[root@RCD sched]# ./get_sched 6799
NORMAL, 0
[root@RCD sched]# ./set_sched 6799 99
FIFO, 99
[root@RCD sched]# ./get_sched 6799
FIFO, 99
- 2.6.24内核中有2种调度器:完全公平调度器CFS和实时调度器RT
-
普通进程默认走的是完全公平调度,在
sched_fork
函数可以看到:
void sched_fork(struct task_struct *p, int clone_flags)
{
// 初始化task_struct
__sched_fork(p);
......
p->prio = current->normal_prio;
// task_struct注册CFS调度器
if (!rt_prio(p->prio))
p->sched_class = &fair_sched_class;
......
- 通过
fork()
创建的子进程会继承父进程的调度策略和优先级,并且在exec()
调用中会保持
// sched_setscheduler()根据调度策略设置调度器
static void
__setscheduler(struct rq *rq, struct task_struct *p, int policy, int prio)
{
BUG_ON(p->se.on_rq);
p->policy = policy;
switch (p->policy) {
case SCHED_NORMAL:
case SCHED_BATCH:
case SCHED_IDLE:
p->sched_class = &fair_sched_class;
break;
case SCHED_FIFO:
case SCHED_RR:
p->sched_class = &rt_sched_class;
break;
}
p->rt_priority = prio;
p->normal_prio = normal_prio(p);
/* we are holding p->pi_lock already */
p->prio = rt_mutex_getprio(p);
set_load_weight(p);
}
而这两个调度器分别定义在sched_fair.c
和sched_rt.c
,指定了对应的实现函数:
static const struct sched_class fair_sched_class = {
.next = &idle_sched_class,
.enqueue_task = enqueue_task_fair,
.dequeue_task = dequeue_task_fair,
.yield_task = yield_task_fair,
.check_preempt_curr = check_preempt_wakeup,
.pick_next_task = pick_next_task_fair,
.put_prev_task = put_prev_task_fair,
#ifdef CONFIG_SMP
.load_balance = load_balance_fair,
.move_one_task = move_one_task_fair,
#endif
.set_curr_task = set_curr_task_fair,
.task_tick = task_tick_fair,
.task_new = task_new_fair,
};
const struct sched_class rt_sched_class = {
.next = &fair_sched_class,
.enqueue_task = enqueue_task_rt,
.dequeue_task = dequeue_task_rt,
.yield_task = yield_task_rt,
.check_preempt_curr = check_preempt_curr_rt,
.pick_next_task = pick_next_task_rt,
.put_prev_task = put_prev_task_rt,
#ifdef CONFIG_SMP
.load_balance = load_balance_rt,
.move_one_task = move_one_task_rt,
#endif
.set_curr_task = set_curr_task_rt,
.task_tick = task_tick_rt,
};
下一篇: 探索设计模式的世界:桥接模式详解