目录

背景

1. 概述

2. 抢占

2.1 用户抢占

2.1.1 抢占触发点

2.1.2 抢占执行点

2.2 内核抢占

2.2.1 抢占触发点

2.2.2 抢占执行点

2.3 preempt_count

3. 上下文切换

3.1 __schedule()

3.2 context_switch()


背景

  • Read the fucking source code!  --By 鲁迅

  • A picture is worth a thousand words. --By 高尔基

说明:

  1. Kernel版本:4.14

  2. ARM64处理器,Contex-A53,双核

  3. 使用工具:Source Insight 3.5, Visio

1. 概述

进程切换:内核将CPU上正在运行的进程挂起,选择下一个进程来运行。ARM架构中,CPU上一次只能运行一个任务,内核需要为任务分配运行时间来进行调度,以便同时能处理多个任务请求。如下图所示:

当进行任务切换的时候,思考下两个问题:

  1. 怎样通过抢占来实现进程的切换?

  2. 当进程切换的时候,到底切换的什么,是怎么实现的?

这两个问题,也是本文探讨的主题了。

2. 抢占

2.1 用户抢占

2.1.1 抢占触发点

  • 可以触发抢占的情况很多,比如进程的时间片耗尽进程等待在某些资源上被唤醒时进程优先级改变等。Linux内核是通过设置TIF_NEED_RESCHED标志来对进程进行标记的,设置该位则表明需要进行调度切换,而实际的切换将在抢占执行点来完成。

不看代码来讲结论,那都是耍流氓。先看一下两个关键结构体:struct task_structstruct thread_info。我们在前边的文章中也讲过struct task_struct用于描述任务,该结构体的首个字段放置的正是struct thread_infostruct thread_info结构体中flag字段就可用于设置TIF_NEED_RESCHED,此外该结构体中的preempt_count也与抢占相关。

struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK/** For reasons of header soup (see current_thread_info()), this* must be the first element of task_struct.*/struct thread_info    thread_info;
#endif...
}/** low level task data that entry.S needs immediate access to.*/
struct thread_info {unsigned long    flags;    /* low level flags */mm_segment_t    addr_limit;  /* address limit */
#ifdef CONFIG_ARM64_SW_TTBR0_PANu64      ttbr0;    /* saved TTBR0_EL1 */
#endifint      preempt_count;  /* 0 => preemptable, <0 => bug */
};#include <asm/current.h>
#define current_thread_info() ((struct thread_info *)current)   //通过该宏可以直接获取thread_info的信息
#endif

看看具体哪些函数过程中,设置了TIF_NEED_RESCHED标志吧:

  • 内核提供了set_tsk_need_resched函数来将thread_infoflag字段设置成TIF_NEED_RESCHED

  • 设置了TIF_NEED_RESCHED标志,表明需要发生抢占调度;

2.1.2 抢占执行点

用户抢占:抢占执行发生在进程处于用户态。抢占的执行,最明显的标志就是调用了schedule()函数,来完成任务的切换。具体来说,在用户态执行抢占在以下几种情况:

  1. 异常处理后返回到用户态;

  2. 中断处理后返回到用户态;

  3. 系统调用后返回到用户态;

如下图:

  • ARMv8有4个Exception Level,其中用户程序运行在EL0,OS运行在EL1,Hypervisor运行在EL2,Secure monitor运行在EL3;

  • 用户程序在执行过程中,遇到异常或中断后,将会跳到ENTRY(vectors)向量表处开始执行;

  • 返回用户空间时进行标志位判断,设置了TIF_NEED_RESCHED则需要进行调度切换,没有设置该标志,则检查是否有收到信号,有信号未处理的话,还需要进行信号的处理操作;

2.2 内核抢占

Linux内核有三种内核抢占模型,先上图:

  • CONFIG_PREEMPT_NONE:不支持抢占,中断退出后,需要等到低优先级任务主动让出CPU才发生抢占切换;

  • CONFIG_PREEMPT_VOLUNTARY:自愿抢占,代码中增加抢占点,在中断退出后遇到抢占点时进行抢占切换;

  • CONFIG_PREEMPT:抢占,当中断退出后,如果遇到了更高优先级的任务,立即进行任务抢占;

2.2.1 抢占触发点

  • 在内核中抢占触发点,也是设置struct thread_infoflag字段,设置TIF_NEED_RESCHED表明需要请求重新调度。

  • 抢占触发点的几种情况,在用户抢占中已经分析过,不管是用户抢占还是内核抢占,触发点都是一致的;

2.2.2 抢占执行点

内核抢占:抢占执行发生在进程处于内核态。

总体而言,内核抢占执行点可以归属于两大类:

  • 中断执行完毕后进行抢占调度;

  • 主动调用preemp_enableschedule等接口的地方进行抢占调度;

2.3 preempt_count

  • Linux内核中使用struct thread_info中的preempt_count字段来控制抢占。

  • preempt_count低8位用于控制抢占,当大于0时表示不可抢占,等于0表示可抢占。

  • preempt_enable()会将preempt_count值减1,并判断是否需要进行调度,在条件满足时进行切换;

  • preempt_disable()会将preempt_count值加1;

此外,preemt_count字段还用于判断进程处于各类上下文以及开关控制等,如图:

3. 上下文切换

  • 进程上下文:包含CPU的所有寄存器值、进程的运行状态、堆栈中的内容等,相当于进程某一时刻的快照,包含了所有的软硬件信息;

  • 进程切换时,完成的就是上下文的切换,进程上下文的信息会保存在每个struct task_struct结构体中,以便在切换时能完成恢复工作;

进程上下文切换的入口就是__schedule(),分析也围绕这函数展开。

3.1 __schedule()

__schedule()函数调用分析如下:

主要的逻辑:

  • 根据CPU获取运行队列,进而得到运行队列当前的task,也就是切换前的prev;

  • 根据prev的状态进行处理,比如pending信号的处理等,如果该任务是一个worker线程还需要将其睡眠,并唤醒同CPU上的另一个worker线程;

  • 根据调度类来选择需要切换过去的下一个task,也就是next

  • context_switch完成进程的切换;

3.2 context_switch()

context_switch()的调用分析如下:

核心的逻辑有两部分:

  • 进程的地址空间切换:切换的时候要判断切入的进程是否为内核线程,1)所有的用户进程都共用一个内核地址空间,而拥有不同的用户地址空间;2)内核线程本身没有用户地址空间。在进程在切换的过程中就需要对这些因素来考虑,涉及到页表的切换,以及cache/tlb的刷新等操作。

  • 寄存器的切换:包括CPU的通用寄存器切换、浮点寄存器切换,以及ARM处理器相关的其他一些寄存器的切换;

** 进程的切换,带来的开销不仅是页表切换和硬件上下文的切换,还包含了Cache/TLB刷新后带来的miss的开销。

** 在实际的开发中,也需要去评估新增进程带来的调度开销。

LoyenWang LoyenWang

https://mp.weixin.qq.com/s/_5FcTa_W19ZrjVs_QWRH3w

(3)Linux进程调度-进程切换相关推荐

  1. linux kernel进程切换(寄存器保存与恢复)

    在threadA执行时,被调度了执行了threadB, 那么在threadB执行完之后是如何恢复threadA进程能够继续执行的呢? 我们从代码中去寻找答案, 系统调度是schedule()函数,那么 ...

  2. Linux进程管理+内存管理:进程切换的TLB处理(ASID-address space ID、PCID-process context ID)

    目录 一.前言 二.单核场景的工作原理 1.block diagram 2.绝对没有问题,但是性能不佳的方案 3.如何提高TLB的性能? 4.特殊情况的考量 4.进一步提升TLB的性能 - ASID( ...

  3. (7)Linux进程调度-O(1)调度算法

    <(1)Linux进程调度> <(2)Linux进程调度器-CPU负载> <(3)Linux进程调度-进程切换> <(4)Linux进程调度-组调度及带宽控制 ...

  4. Linux进程调度:完全公平调度器 Completely Fair Scheduler 内幕| linux-2.6

    https://www.ibm.com/developerworks/cn/linux/l-completely-fair-scheduler/index.html? 目录 Linux 调度器简史 C ...

  5. Linux前台后台进程切换(转载)

    linux前后台进程切换(转) (2012-05-10 12:31:20) 转载▼ 标签: 杂谈 分类: linux操作系统 from:http://blog.csdn.net/java3344520 ...

  6. Linux内核进程调度的时机和进程切换

    陈铁+ 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 对于现代操作系统,多 ...

  7. Linux内核进程管理基本概念-进程、运行队列、等待队列、进程切换、进程调度

    下面简述一些基本概念,以及对内核代码做最初步的了解: 一 Linux内核进程管理基础 Linux 内核使用 task_struct 数据结构来关联所有与进程有关的数据和结构,Linux 内核所有涉及到 ...

  8. linux swi 内核sp,Linux内核分析课程8_进程调度与进程切换过程

    8种机械键盘轴体对比 本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选? Linux内核课第八周作业.本文在云课堂中实验楼完成. 原创作品转载请注明出处 <Linux内核分析>MOO ...

  9. Linux 进程管理之进程调度与切换

    进程调度相关内核结构 我们知道,进程运行需要各种各样的系统资源,如内存.文件.打印机和最 宝贵的 CPU 等,所以说,调度的实质就是资源的分配.系统通过不同的调度算法(Scheduling Algor ...

最新文章

  1. Objective-C中的block块语法
  2. HDU2888(二维RMQ)
  3. java护眼色是什么数据,护眼色的RGB值和颜色代码汇总
  4. 面向对象设计原则之3-里氏替换原则
  5. MVC教程第六篇:拦截器
  6. java基础—统计一个字符串中各个字符出现的次数
  7. 【连载】Django入门到实战(一)
  8. shiro登陆成功后被拦截_Springboot+Shiro+redis整合
  9. 三维旋转矩阵_第三讲:三维空间的刚体运动
  10. melt函数_熊猫的melt()和使用pivot()函数取消融化
  11. 关于游戏开发流程解析
  12. python绘制彩色地震剖面断层解释_地震剖面上断层的识别标志主要有哪些
  13. 12306验证码破解思路分享
  14. django 设置媒体url_django-文件上传Media url的配置
  15. word中生成带方块的对勾
  16. 北京华宇南航宣讲面试
  17. 结构体知识——声明,初始化,成员访问,传参
  18. 基于mean-shift的简单目标跟踪
  19. 台湾各个大学硕博论文链接,很全,有的可以全文下载。
  20. 北京“众合天下”连续八年发布中国企业社保白皮书

热门文章

  1. 【MySQL通过视图(或临时表)实现动态SQL(游标】
  2. 【Vue-router中,require代替import解决vue项目首页加载时间过久】
  3. bzoj1132:[POI2008]Tro
  4. Scala之集合Collection
  5. 数据库:跨数据库,服务器数据迁移
  6. samba-设定文件共享
  7. 网上看到的,关于测试用例编写粒度准则
  8. ASP.NET中Session简单原理图
  9. json-lib 的maven dependency
  10. linux内核学习笔记【一】临时内核页表 Provisional kernel Page Tables