浅析 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/...

相关推荐
2401_826097623 小时前
JavaEE-Linux环境部署
java·linux·java-ee
(:满天星:)4 小时前
第31篇:块设备与字符设备管理深度解析(基于OpenEuler 24.03)
linux·运维·服务器·网络·centos
爱莉希雅&&&4 小时前
shell编程之awk命令详解
linux·服务器·git
笑稀了的野生俊4 小时前
在服务器中下载 HuggingFace 模型:终极指南
linux·服务器·python·bash·gpu算力
渡我白衣5 小时前
Linux操作系统之文件(四):文件系统(上)
linux
ZZH1120KQ5 小时前
Linux系统安全及应用
linux·运维·系统安全
程序漫游人5 小时前
centos8.5安装jdk21详细安装教程
java·linux
小小小糖果人6 小时前
Linux云计算基础篇(5)
linux·运维·服务器
small_wh1te_coder6 小时前
硬件嵌入式学习路线大总结(一):C语言与linux。内功心法——从入门到精通,彻底打通你的任督二脉!
linux·c语言·汇编·嵌入式硬件·算法·c
小张是铁粉6 小时前
docker在Linux的安装遇到的问题
linux·docker·容器