浅析 Linux 内核进程调度

1. 前言

进程调度是操作系统中一个重要的管理任务,它决定在给定时间点哪个进程可以被调度执行。进程调度的目标是最大程度地利用系统资源、提高系统吞吐量、降低响应时间以及确保公平性和优先级的合理分配。

Linux 内核提供了强大的进程调度能力以及丰富的调度策略。让 Linux 系统可以轻松应对 CPU 密集型、实时以及 IO 密集型等各种各样的计算任务。

本文主要通过结合上文提供的流程图,简要介绍一下 Linux 内核中进程的调度逻辑。

2. 基础数据结构

2.1 进程描述符

在 Linux 中描述进程的数据结构是task_strct,里面包含了进程描述信息、分配给进程的内存资源、进程打开的文件列表以及进程调度相关的信息。下面是节选了跟进程调度相关的字段。

c 复制代码
struct task_struct {
        /*
         * 进程优先级
         */
	int				prio;
	int				static_prio;
	int				normal_prio;
	unsigned int			rt_priority;
        /*
         * 进程关联的调度实体
         */
	struct sched_entity		se;
	struct sched_rt_entity		rt;
	struct sched_dl_entity		dl;
        /*
         * 进程关联的调度类
         */
	const struct sched_class	*sched_class;
}

2.2 调度实体

调度实体(sched_entity),在 Linux 内核中所有可以被调度器调度的对象都会有一个对应的调度实体。

这里需要注意的是,调度实体并不是一个结构体,而是一系列结构的总称。它包括sched_entitysched_rt_entity以及sched_dl_entity等。其中sched_entity是被CFS调度类使用,而sched_rt_entity则为RT调度类使用。

2.3运行队列RQ

rq 结构体(struct rq)是一个进程调度一个核心的数据结构,它表示一个运行队列。其用于跟踪和管理在特定 CPU 上等待运行的进程和线程。rq 结构体定义了一系列的字段,这些字段用于存储和管理关于 CPU 调度的各种信息。包括当前正在运行的任务、等待运行的任务队列、调度器统计信息、负载平衡信息等。

2.4 调度类

在 Linux 内核中调度类负责实际执行下一个要运行进程的挑选工作。sched_class主要由一系列函数指针构成,这些函数指针由具体的调度类负责实现。

c 复制代码
struct sched_class {
    const struct sched_class *next; // 指向下一个调度类的指针

    void (*enqueue_task)(struct rq *rq, struct task_struct *p, int flags);
    void (*dequeue_task)(struct rq *rq, struct task_struct *p, int flags);
    void (*check_preempt_curr)(struct rq *rq, struct task_struct *p, int flags);

    struct task_struct *(*pick_next_task)(struct rq *rq, struct task_struct *prev);

    ...
};
  • enqueue_task:将一个进程添加进队列。
  • dequeue_task:将一个进程从队列里面移除。
  • check_preempt_curr:判断当前进程是否可以被抢占。
  • pick_next_task:选择下一个要运行的进程。

3. 调度流程

Linux 内核中进程的调度入口函数是schedule函数,在schedule函数的逻辑中,首先会提取出当前进程所运行的 CPU,然后从 CPU 中取出关联的 rq 运行队列。

在 Linux 内核中rq是一个per-CPU变量,会为每个 CPU 保存一份单独的拷贝,避免了多个 CPU 之间的数据竞争。per-CPU变量的实现具体取决与 CPU 的架构,有些 CPU 架构是存放与 CPU 的本地存储器(local memory)中。有些实现在是存放与一个特殊的段(Segment)中。

由于2.3小节的介绍可知,rq是 CPU 表征运行队列的结构体。所有等待CPU调度的进程都会在rq中排队(排队的队列可能是FIFO队列,也可能是优先级队列,具体取决于调度类)。

Linux 内核提供了多种调度策略,它们被抽象为一个个调度类。目前,内核中提供的调度类包括停机调度类(stop_class)、限期调度类(dl_class)、实时调度类(rt_class)、完全公平调度类(cfs_class)等等。

在 Linux 内核中维护了一个全局的调度类数组sched_class,当开始挑选下一个进程上CPU运行时,内核代码会遍历此数组,分别调用这些调度类上定义的pick_next_task函数获取下一个要运行的进程。这也意味着排在数组前面的调度类进程更有机会进入CPU运行。这也是调度类优先级实现的机制。排在数组前面的调度类优先级越高。

当挑选出下一个运行的进程之后,内核代码会进入CPU上下文切换流程调用context_switch函数。自此,进程的调度工作已经完成。

4. 总结

本文是我第一次写 Linux 内核相关的博文,内容上难免存在纰漏和瑕疵。如能邮件指出不胜感激。

5. 参考资料

6. 文章链接

www.zhuoxiong.top/2023/12/28/...

相关推荐
刘某的Cloud1 小时前
parted磁盘管理
linux·运维·系统·parted
啊?啊?1 小时前
4 解锁 Linux 操作新姿势:man、grep、tar ,创建用户及添加权限等 10 大实用命令详解
linux·服务器·实用指令
程序员老舅1 小时前
干货|腾讯 Linux C/C++ 后端开发岗面试
linux·c语言·c++·编程·大厂面试题
爱倒腾的老唐1 小时前
24、Linux 路由管理
linux·运维·网络
程序员Aries1 小时前
自定义网络协议与序列化/反序列化
linux·网络·c++·网络协议·程序人生
泽02022 小时前
Linux之环境变量
java·linux·redis
正在努力的小河2 小时前
Linux 自带的 LED 灯驱动实验
linux·运维·服务器
檀越剑指大厂3 小时前
【Linux系列】Vim 中删除当前光标到行尾
linux·运维·vim
qiuiuiu4134 小时前
正点原子RK3568学习日志-编译第一个驱动程序helloworld
linux·c语言·开发语言·单片机
林开落L4 小时前
线程进阶:线程池、单例模式与线程安全深度解析
linux·安全·单例模式·线程池