操作系统学习-4.Linux进程调度学习1-进程调度原理

简单分析

假设我的系统只有一种调度算法cfs

那么有个调度的队列 cfs_rq

所有running的进程都会 进入这个队列,不在running 或者其他情况会出队列,ok。则假设队列控制的算法有以下。

cfs_rq_enqueue
cfs_rq_dequeue
cfs_rq_pick

所操作的是进程描述符 task_struck.

那么很简单可以理解上述过程

scehed_pick ---->cfs_rq_pick就行了。

多个调度算法

那么如果除了cfs还有rt算法

那么就有两个调度队列,cfs_rq和rt_rq。

一个进程task_struck 有可能属于cfs和rt。

那么考虑 scehed_pick 是如何pick?

ok,Linux建立一个sched_class 的结构链表,sched_class_cfs和sched_class_rt或者还有其他的。顺序的从这么多个调度算法中选择一个合适的。

stop_sched_class -> -> rt_sched_class -> fair_sched_class -> idle_sched_class

如上。那么问题来了,如果前面的队列一直满足,后面的队列就永远得不到执行,这些sched_class之间没有个合理的逻辑吗?

目前看到的逻辑,任务dl 是最先满足的,rt次之,cfs随后,idle当然是最后的,这样的逻辑,基本上能让人有点信服。

再抽象一层sched_entity,

一般情况 cfs和rt或者其他的什么的调度算法的接口 enqueue或者dequeue 都是对task_struck 进行操作的。

但是Linux 这里再抽象一个sched_entity,每个task_struck 对应一个sched_entity 。调度算法对sched_entity操作就行了。

这样抽象我猜想有两个目的,一个是统一比较好看,和task_struck隔离。第二个是,为了下面的组调度做准备。

加入组调度

组调度的数据结构,和组织架构大概是如下这个样子
在这里插入图片描述
在这里插入图片描述
OK,如果Linux进行组调度,就不会说使用全局的cfs_rq队列,或者rt_rq队列。而是将这些队列分配到task_group中。大概流程是这样子的。

我们假设我们有两个组 GroupA 和GroupB A占2 B占1 就是有三次调用的情况下 A组会被调用两次,B组只有1次。

在这里插入图片描述
这个时候有一个进程启动了 task_struck task1

在这里插入图片描述
他选择A组,同理task2 可能也选择A task3可能选择B 如下

在这里插入图片描述
而A 和 B 不会记录 task的接口体,他记录task 的sched_entity 并用一个se[] 数组表示。

那么还有,task1 task2 有可能是cfs调度也有可能是rt,那么gruop结构体就用 cfs_se[] cfs_rq 和 rt_se[] rt_rq记录。

那么问题来了

task1 task2 属于cfs还是属于rt 是什么时候设置的?

在Android和linux里面没有看到,目前看到的是 0 也就是cfs,
那么有以下可能就是说,如果你不设置,就默认是0,或者继承父亲的等等这种默认策略。

scehed_pick 时候怎么pick?

按照pick三次 两次是A,ok。到了A,再使用这个策略

stop_sched_class -> dl_sched_class -> rt_sched_class -> fair_sched_class -> idle_sched_class

这个是说的通的。

但是要根据代码来。

组调度基本思想

来看下组调度

static struct task_struct *pick_next_task_fair(struct rq *rq)
{
      struct task_struct *p;
      struct cfs_rq *cfs_rq = &rq->cfs;
      struct sched_entity *se;
     
      if (!cfs_rq->nr_running)
        return NULL;
     
      do {
        se = pick_next_entity(cfs_rq);
        set_next_entity(cfs_rq, se);
        cfs_rq = group_cfs_rq(se);
      } while (cfs_rq);
     
      p = task_of(se);
      if (hrtick_enabled(rq))
        hrtick_start_fair(rq, p);
     
      return p;
    }

代码很简单,其实在2.4以前是没有组调度的。就不会有group_cfs_rq这个队列,那么所有task都在全局的rq->cfs,就是刚开始的。

后面为了引入分组的概念,就有了组调度。

引入分组的概念后,原来对task直接进行的操作就变得不那么遍历,就引入了sched_entity。整体结构如下。

在这里插入图片描述
结合代码理解下

  • 首先代码 struct cfs_rq *cfs_rq = &rq->cfs; 拿到全局的cfs_rq。

  • 然后进行一次pick 拿到sched_entity se(假设是A cfs_rq) -----------sched_entity 和 task_group是可以相互转换的

  • 然后把全局的cfs_rq 复制成现在的cfs_rq(假设是A cfs_rq)

  • 然后是while循环,如果A下面没有节点了,就结束循环,但是A下面还有节点,就循环,再重新赋值,如此如此。知道根部。

  • 那么假设我们在A的下面 task_gruop1 停止了,因为它的cfs_rq是空的。

  • 接下来就把task_group1 转换成task_struck p。

  • 最后就结束返回。

同理rt队列也是这样的:如果主调度器(),选择的是rt_sched_class 那就 pick_next_task_rt

那么就会从task_group A 的rt_rq开始调度

task_group不仅仅有cfs_rq 还有 rt_rq。

关于task_group 和组调度的详细我们接下来会详细分析。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要分析Linux内核的进程调度源码,首先需要了解Linux内核的源代码结构和相关概念。进程调度操作系统的核心功能之一,负责决定何时以及如何将CPU分配给不同的进程。 在Linux内核中,进程调度的相关源码位于kernel/sched目录下。该目录包含了与进程调度相关的各个文件和子目录,其中最重要的文件是sched.h和sched.c。 sched.h定义了与进程调度相关的数据结构、宏定义和函数原型。你可以在该文件中找到与调度算法相关的定义,如进程状态、调度类别、优先级等。 sched.c是进程调度的核心代码文件,其中实现了各种调度算法。在该文件中,你可以找到与进程调度相关的函数实现,如schedule()函数,这是Linux内核的调度函数,用于选择下一个要运行的进程。 除了sched.h和sched.c,还有一些其他的文件和子目录也与进程调度相关,如fair.c、rt.c和core.c等。fair.c中实现了CFS(完全公平调度)算法,rt.c实现了实时进程调度算法,core.c包含了通用的调度函数。 要深入理解Linux内核的进程调度源码,你可以通过阅读相关文件和函数的注释来获取更多信息。此外,还可以参考一些相关的内核文档和书籍,如《Linux内核源代码情景分析》和《深入理解Linux内核》等。 请注意,分析内核源码需要有一定的操作系统和编程经验,并且需要花费相当的时间和精力。建议在开始分析之前,先熟悉相关的概念和基本的内核知识。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沈万三djh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值