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

相关推荐
大G哥19 分钟前
python 数据类型----可变数据类型
linux·服务器·开发语言·前端·python
BillKu44 分钟前
Linux设置Nginx开机启动
linux·运维·nginx
baidu_375528811 小时前
光感传感器 芯片stk3171 linux驱动程序
linux·运维·服务器
飞腾开发者1 小时前
飞腾平台Arm NN软件栈安装使用指南
linux·运维·人工智能·机器学习·计算机视觉
夜暝1 小时前
Iotop使用
linux
鸠摩智首席音效师2 小时前
.NET Core 应用程序如何在 Linux 中创建 Systemd 服务 ?
linux·运维·.netcore
不是三毛没有半2 小时前
Centos 7 安装wget
linux
叫我龙翔3 小时前
【计网】实现reactor反应堆模型 --- 多线程方案优化 ,OTOL方案
linux·运维·网络
mit6.8243 小时前
[Docker#9] 存储卷 | Volume、Bind、Tmpfs | -v/mount | MySQL 灾难恢复 | 问题
linux·运维·docker·容器·架构
WangYaolove13143 小时前
请解释Python中的装饰器是什么?如何使用它们?
linux·数据库·python