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

相关推荐
多吃蔬菜!!!38 分钟前
vscode 搭建C/C++开发环境搭建(linux)
linux·c语言·c++
李李李li1 小时前
Ubuntu 22.04 安装tensorrt
linux·tensorrt
phoenix09813 小时前
Linux入门DAY29
linux·运维
入秋3 小时前
Linux服务器安装部署 Nginx、Redis、PostgreSQL、Docker
linux·前端
Mr. Cao code3 小时前
使用Tomcat Clustering和Redis Session Manager实现Session共享
java·linux·运维·redis·缓存·tomcat
zcz16071278214 小时前
Linux 网络命令大全
linux·运维·网络
the sun344 小时前
Reactor设计模式及其在epoll中的应用
linux·运维·服务器·c++
喜欢你,还有大家4 小时前
Linux笔记7——shell编程基础-1
linux·运维·笔记
运维成长记4 小时前
Top 100 Linux Interview Questions and Answers
linux·运维·服务器
人工智能训练师4 小时前
openEuler系统中如何将docker安装在指定目录
linux·运维·服务器·人工智能·ubuntu