(代码主要参考5.10)

1. __schedule的参数preempt

static void __sched notrace __schedule(bool preempt)

preempt是一个bool的类型的值。

__schedule中有这样的一段代码,(有删减):

  1. switch_count = &prev->nivcsw;

    prev_state = prev->state;
    if (!preempt && prev_state) {
    if (signal_pending_state(prev_state, prev)) {
    prev_state = TASK_RUNNING;
    } else {
    ......;
    deactivate_task(rq, prev, DEQUEUE_SLEEP | DEQUEUE_NOCLOCK);
    ......;
    }
    switch_count = &prev->nvcsw;
    }

    ......;
    if (likely(prev != next)) {
    ......;
    ++switch_context;
    }

preempt代表是否自愿上下文切换。如果是自愿(非抢占进行调度),则为false;如果是非自愿(抢占进行调度),则为true。

struct task_struct有两个成员nvcswnivcsw

nvcsw nivcsw
Number of Voluntary Context Switches(自愿上下文切换的计数) Number of InVoluntary Context Switches(非自愿上下文切换计数)

当一个进程非自愿上下文切换的时候,即被抢占的时候,会少判断一些内容;

而当一个进程自愿上下文切换的时候,即主动放弃CPU的时候,要进行一些判断,会决定prev的状态,是否出队,以及负载均衡的一些操作,这里就不详细描述了。

至于哪些函数,会触发调度__schedule,它们分别是抢占还是非抢占呢?5.10中如下所示:

function preempt
do_task_dead false
schedule false
schedule_idle false
preempt_schedule_comm true
preempt_schedule_notrace true
preempt_schedule_irq true

这些函数留给后续分析吧。

2. pick_next_task的两条路径

pick_next_task函数在__schedule中调用,挑选下一个要执行的进程。

  1. static inline struct task_struct *
    pick_next_task(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
    {
    ......;
    if (likely(prev->sched_class <= &fair_sched_class &&
    rq->nr_running == rq->cfs.h_nr_running)) {---短路径
    p = pick_next_task_fair(rq, prev, rf);
    ......;
    }
    restart:
    ......;
    for_each_class(class) {---长路径
    p = class->pick_next_task(rq);
    ......;
    }
    }

是走长路径、还是短路径呢?判断条件为:当前进程的调度类是否为cfs或者idle以及运行队列的进程数量是否与cfs运行队列的进程数量相等。

cfs_rq中除了h_nr_running外,还有一个nr_running,以及rq中也存在一个nr_running,它们分别代表什么?

成员 解释
rq的nr_running 代表运行队列的进程个数
cfs_rq的nr_running 开启组调度的话,代表组调度最上层的group个数
cfs_rq的h_nr_running 代表cfs_rq中的进程个数

3. context_switch的四种情况

挑选出下一个要执行的进程next后,要使用context_switch进行地址空间的切换。

  1. static __always_inline stuct rq *
    context_switch(struct rq *rq, struct task_struct *prev,
    struct task_struct *next, struct rq_flags *rf)
    {
    ......;
    if (!next->mm) {
    enter_lazy_tlb(prev->active);

    next->active_mm = prev->active_mm;
    if (prev->mm)
    mmgrab(prev->active_mm);
    else
    prev->active_mm = NULL;
    } else {
    membarrier_switch_mm(rq, prev->active_mm, next->mm);

    switch_mm_irqs_off(prev->active_mm, next->mm, next);

    if (!prev->mm) {
    rq->prev_mm = prev->active_mm;
    prev->active_mm = NULL;
    }
    }
    ......;
    switch_to(prev, next, prev);
    barrier();

    return finish_task_switch(prev);
    }

所谓的四种情况,其实就是prev和next分别是user线程还是kernel线程的组合情况。

prev next 操作
kernel kernel tlb lazy模式,next借用prev的active_mm,prev的active_mm清空
user kernel tlb lazy模式,next借用prev的active_mm,prev的mm_count增加计数
kernel user 地址空间切换,rq记录prev_mm,将prev->active_mm清空
user user 进程地址空间切换

Q1:什么是tlb lazy模式?

tlb是什么?是一个虚拟地址转换成物理地址的快速转换表,常用于cache寻址中。

通常CPU都是进程切换一次,进行一次flush(后面有其他不用全部flush的方法,不详细描述了)。

而内核空间是所有进程通用的,故可以不用flush tlb,这就是tlb lazy模式。

Q2:mmactive_mm区别?

mm的存在与否用于判定该进程是属于user还是kernel;

active_mm则为实际使用的地址空间,kernel线程总是借用user线程的地址空间。

可以看到,每次kernel线程被切换出去后,它的active_mm就会被清空,因为是借用的;而每次user线程切换kernel的时候,还会增加一个计数值,用于表示该user线程的地址空间被借用了。

4. switch_to的三个参数

switch_to的工作主要是切换内核栈,它的具体实现就不在这里分析。

不同的体系架构下也不一样,例如,X86的实现主要使用将当前寄存器的一些值压到prev的内核栈,将内核栈顶指针保存到每个进程相关联的thread_info,然后切换到next的内核栈,并出栈,将其栈中内容填充到寄存器,以恢复现场。

switch_to三个参数,其中两个prev的考虑:

设想场景如下:a切换到b,b切换到c,c切换到a。

a压栈时内容:prev为a,next为b;

b压栈时内容:prev为b,next为c;

c压栈时内容:prev为c,next为a。

c切换a,出栈之后呢?

prev为a,next为b

可以看到完全没有c的事了。我们必须得留下c存在过的痕迹。

故这里使用三个参数,其中两个prev,用来留下最新的prev的痕迹。

5. finish_task_switch与context_switch的联动

为什么一定要留下最新的prev的痕迹呢?你有没有想过?

finish_task_switch的参数就是prev,就是因为它使用到了prev,所以才得留下它。

finsih_task_switch其实涉及到的东西蛮多,计算vtime,perf追踪点等等。

但就在进程调度过程中,还有一个细节没有处理,还记得是什么吗,参见finish_task_switch的部分代码:

  1. struct mm_struct *mm = rq->prev_mm;

    ......;

    if (mm) {
    membarrier_mm_sync_core_before_usermode(mm);
    mmdrop(mm);

    }

如果从user到kernel,那么得给借用的mm增加一个计数;

但是什么时候减去呢?

一旦从kernel到user,rq就记录下prev使用的active_mm,在finish_task_switch中减去这个计数。

有道词典

static void __s ...

详细X

静态孔隙__sched notrace __schedule (bool抢占)

__schedule的一些小细节的更多相关文章

  1. Oracle Sales Cloud:管理沙盒(定制化)小细节2——使用对象触发器更新数字字段

    在上一篇 "管理沙盒(定制化)小细节1" 的随笔中,我们使用公式法在 "业务机会" 对象(单头)上建立了 "利润合计" 字段,并将它等于 & ...

  2. Oracle Sales Cloud:管理沙盒(定制化)小细节1——利用公式创建字段并显示在前端页面

    Oracle Sales Cloud(Oracle 销售云)是一套基于Oracle云端的CRM管理系统.由于 Oracle 销售云是基于 Oracle 云环境的,它与传统的管理系统相比,显著特点之一便 ...

  3. Oracle Sales Cloud:报告和分析(BIEE)小细节2——利用变量和过滤器传参(例如,根据提示展示不同部门的数据)

    在上一篇随笔中,我们建立了部门和子部门的双提示,并将部门和子部门做了关联.那么,本篇随笔我们重点介绍利用建好的双提示进行传参. 在操作之前,我们来看一个报告和分析的具体需求: [1] 两个有关联的提示 ...

  4. Oracle Sales Cloud:报告和分析(BIEE)小细节1——创建双提示并建立关联(例如,部门和子部门提示)

    Oracle Sales Cloud(Oracle 销售云)是一套基于Oracle云端的客户商机管理系统,通过提供丰富的功能来帮助提高销售效率,更好地去了解客户,发现和追踪商机,为最终的销售成交 (d ...

  5. php课程---Json格式规范需要注意的小细节

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式. 易于人阅读和编写.同时也易于机器解析和生成. 它基于JavaScript Programming Lan ...

  6. ASP.NET MVC 自定义路由中几个需要注意的小细节

    本文主要记录在ASP.NET MVC自定义路由时,一个需要注意的参数设置小细节. 举例来说,就是在访问 http://localhost/Home/About/arg1/arg2/arg3 这样的自定 ...

  7. [小细节,大BUG]记录一些小问题引起的大BUG(长期更新....)

    [小细节,大BUG] 6.问题描述:当从Plist文件加载数据,放入到tableView中展示时,有时有数据,有时又没有数据.这是为什么呢?相信很多大牛都想到了:我们一般将加载的数据,转换成模型,放入 ...

  8. C++在使用Qt中SLOT宏须要注意的一个小细节

    大家都知道C++虚函数的机制,对于基类定义为虚函数的地方,子类假设覆写,在基类指针或者引用来指向子类的时候会实现动态绑定. 但假设指针去调用非虚函数,这个时候会调用C++的静态绑定,去推断当前的指针是 ...

  9. SQL小细节

    平时有些小细节,不留意的话很容易得到错误的答案,我们来看下下面的代码,看看你是否能答对呢? ) ,) SELECT @str = '中国CH',@info='MyTest' SELECT [字符串]= ...

随机推荐

  1. jQuery选择器中的特殊符号和关键字

    一般情况下,在jQuery选择器中,我们很少会用到诸如"."."#"."("."["等特殊字符,因为根据W3C规定,HT ...

  2. 深度学习编译与优化Deep Learning Compiler and Optimizer

    深度学习编译与优化Deep Learning Compiler and Optimizer

  3. Mobileye 自动驾驶策略(一)

    Mobileye 自动驾驶策略(一) 详解 Mobileye 自动驾驶解决方案 Mobileye的自动驾驶解决方案.总得来说,分为四种: Visual perception and sensor fu ...

  4. 重新整理 mysql 基础篇————— 介绍mysql日志[二]

    前言 对于后端开发来说,打交道最多的应该是数据库了,因为你总得把东西存起来. 或是mongodb或者redis又或是mysql.然后你发现一个问题,就是他们都有日志系统,那么这些日志用来干什么的呢? ...

  5. java后端知识点梳理——多线程与高并发

    进程与线程 进程是一个"执行中的程序",是系统进行资源分配和调度的一个独立单位 线程是进程的一个实体,一个进程中一般拥有多个线程. 线程和进程的区别 进程是操作系统分配资源的最小单 ...

  6. 「题解」CF1468M Similar Sets

    本文将同步发布于: 洛谷博客: csdn: 博客园: 简书. 题目 题目链接:洛谷.CF1468M. 题意简述 给定 \(n\) 个集合 \(S_{1\sim n}\),问是否存在 \(i,j\) 满 ...

  7. 查找文件与cron计划任务

    查找文件 • 根据预设的条件递归查找对应的文件 find [目录] [条件1] [-a|-o] [条件2] ... -type  类型(f文件.d目录.l快捷方式) -name  "文档名称 ...

  8. 【模拟8.01】string(线段树)

    因为题中只有a-z,所以区间中大量字母都是重复的,我们不妨利用桶的性质. 开一棵树,里面维护当前区间内的相同元素,若区间内元素不同,则为零 每次升序操作就先查询一遍区间,用桶将每个区间的a-z元素统计 ...

  9. ORACLE中的PL/SQL

    一. 1.过程,函数,触发器是pl/sql编写.                2. 过程函数触发器是在Oracle中.                      3.pl/sql是非常强大的数据库过 ...

  10. Kafka 总结学习

    Kafka Need No Keeper 最近在鹅厂工作中不断接触到Kafka,虽然以前也使用过,但是对其架构和发展过程总是模模糊糊,所以在回学校准备末考的时候找些资料总结一下. Kafka Need ...