欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

全面解析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()设置和获取实时进程优先级
  • 调度策略
    • SCHED_NORMAL(也称SCHED_OTHER):用于普通进程,通过完全公平调度器(CFS)处理
    • SCHED_BATCH:用于非交互、CPU使用密集的批处理进程,通过CFS处理
    • SCHED_IDLE:用于相对权重最小的进程,通过CFS处理(注意:SCHED_IDLE不负责调度空闲进程,空闲进程由内核提供单独机制处理)
    • SCHED_RR:用于软实时进程,轮询调度,通过实时调度器类处理
    • SCHED_FIFO:用于软实时进程,先进先出调度,通过实时调度器类处理

image.png

【例】普通进程:获取和修改进程优先级

/* 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, &param) == -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, &param) == -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.csched_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,
};