【原创】(三)Linux进程调度器-进程切换
背景
Read the fucking source code!--By 鲁迅A picture is worth a thousand words.--By 高尔基
说明:
- Kernel版本:4.14
- ARM64处理器,Contex-A53,双核
- 使用工具:Source Insight 3.5, Visio
1. 概述
进程切换:内核将CPU上正在运行的进程挂起,选择下一个进程来运行。
ARM架构中,CPU上一次只能运行一个任务,内核需要为任务分配运行时间来进行调度,以便同时能处理多个任务请求。
如下图所示:

当进行任务切换的时候,思考下两个问题:
- 怎样通过抢占来实现进程的切换?
- 当进程切换的时候,到底切换的什么,是怎么实现的?
这两个问题,也是本文探讨的主题了。
2. 抢占
2.1 用户抢占
2.1.1 抢占触发点
- 可以触发抢占的情况很多,比如进程的时间片耗尽、进程等待在某些资源上被唤醒时、进程优先级改变等。Linux内核是通过设置
TIF_NEED_RESCHED标志来对进程进行标记的,设置该位则表明需要进行调度切换,而实际的切换将在抢占执行点来完成。
不看代码来讲结论,那都是耍流氓。先看一下两个关键结构体:struct task_struct和struct thread_info。我们在前边的文章中也讲过struct task_struct用于描述任务,该结构体的首个字段放置的正是struct thread_info,struct 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_PAN
u64 ttbr0; /* saved TTBR0_EL1 */
#endif
int 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_info中flag字段设置成TIF_NEED_RESCHED; - 设置了
TIF_NEED_RESCHED标志,表明需要发生抢占调度;
2.1.2 抢占执行点
用户抢占:抢占执行发生在进程处于用户态。
抢占的执行,最明显的标志就是调用了schedule()函数,来完成任务的切换。
具体来说,在用户态执行抢占在以下几种情况:
- 异常处理后返回到用户态;
- 中断处理后返回到用户态;
- 系统调用后返回到用户态;
如下图:

- 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_info的flag字段,设置TIF_NEED_RESCHED表明需要请求重新调度。 - 抢占触发点的几种情况,在用户抢占中已经分析过,不管是用户抢占还是内核抢占,触发点都是一致的;
2.2.2 抢占执行点
内核抢占:抢占执行发生在进程处于内核态。

总体而言,内核抢占执行点可以归属于两大类:
- 中断执行完毕后进行抢占调度;
- 主动调用
preemp_enable或schedule等接口的地方进行抢占调度;
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的开销。在实际的开发中,也需要去评估新增进程带来的调度开销。

【原创】(三)Linux进程调度器-进程切换的更多相关文章
- Linux内核分析——理解进程调度时机跟踪分析进程调度与进程切换的过程
20135125陈智威 +原创作品转载请注明出处 +<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验 ...
- linux内核分析第八周-理解进程调度时机跟踪分析进程调度与进程切换的过程
实验原理: 一.调度时机 不同类型的进程有不同的调度需求 第一种分类: I/O-bound 频繁的进行I/O 通常会花费很多时间等待I/O操 ...
- Linux内核设计第八周学习总结 理解进程调度时机跟踪分析进程调度与进程切换的过程
陈巧然 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.视频内容 Linux ...
- 理解进程调度时机跟踪分析进程调度与进程切换的过程(Linux)
----------------------------------------------------------------------------------- 理解进程调度时机跟踪分析进程调度 ...
- Linux内核分析--理解进程调度时机、跟踪分析进程调度和进程切换的过程
ID:fuchen1994 姓名:江军 作业要求: 理解Linux系统中进程调度的时机,可以在内核代码中搜索schedule()函数,看都是哪里调用了schedule(),判断我们课程内容中的总结是否 ...
- 20135202闫佳歆--week 8 实验:理解进程调度时机跟踪分析进程调度与进程切换的过程--实验及总结
week 8 实验:理解进程调度时机跟踪分析进程调度与进程切换的过程 1.环境搭建: rm menu -rf git clone https://github.com/megnning/menu.gi ...
- Linux内核分析之理解进程调度时机跟踪分析进程调度与进程切换的过程
一.原理分析 1.调度时机 背景不同类型的进程有不同的调度需求第一种分类I/O-bond:频繁的进行I/O:通常会花费很多时间等待I/O操作的完成CPU-bound:计算密集型:需要大量的CPU时间进 ...
- Linux进程调度器的设计--Linux进程的管理与调度(十七)
1 前景回顾 1.1 进程调度 内存中保存了对每个进程的唯一描述, 并通过若干结构与其他进程连接起来. 调度器面对的情形就是这样, 其任务是在程序之间共享CPU时间, 创造并行执行的错觉, 该任务分为 ...
- Linux进程调度器概述--Linux进程的管理与调度(十五)
调度器面对的情形就是这样, 其任务是在程序之间共享CPU时间, 创造并行执行的错觉, 该任务分为两个不同的部分, 其中一个涉及调度策略, 另外一个涉及上下文切换. 1 背景知识 1.1 什么是调度器 ...
随机推荐
- HTTP编码
HTTP编码 不仅仅URL需要编码,HTTP header也需要编码,HTTP body 无特殊要求 一般采用百分号编码:比如一个字节的ascii码值是 0x89 那使用百分号编码之后 输出是 %89 ...
- iOS 清理文件缓存
本文摘自:<msp的昌伟哥哥-iOS开发-清理缓存功能的实现>摘下来的目的就是为了能够学习.还望看到文章的同学,前往原创的博客园.感谢msp的昌伟哥哥的分享精神. 移动应用在处理网络资源时 ...
- poj-3665 iCow(暴力吧)
http://poj.org/problem?id=3665 题目描述 Fatigued by the endless toils of farming, Farmer John has decide ...
- SAP 配置表记录创建人/创建日期/创建时间/更改人/更改日期/更改时间
在实际开发需求中,为了使客制功能具有灵活的可配置性,通常采用开发功能+配置表的形式处理.有些客制的配置功能需要追溯到谁在什么时候增加了什么配置,或者谁在什么时候更改了什么位置,配置表的Log功能就显得 ...
- Nginx配置使用
1.黑色标注的得自己写入到nginx.conf文件中 upstream serverlb { server 127.0.0.1:9999; server 127.0.0.1:8888; } serve ...
- 3)利用Build.php自动创建目录和文件
(1)首先做法参照: thinkphp5的手册的 命令行--->自动生成目录结构 或者看云的资料:https://www.kancloud.cn/manual/thinkphp5/118021 ...
- token和refresh token
https://www.cnblogs.com/minirice/p/9232355.html 在spring boot中结合OAuth2使用JWT时,刷新token时refresh token一直变 ...
- 《ECMAScript 6 入门教程 - 阮一峰著》学习笔记
在刷LeetCode的过程中看到很多新的语法糖,系统学习一下以便代码更加规范,美观,健壮.
- mysql之左连接、右连接、内连接、全连接、等值连接、交叉连接等
mysql中的各种jion的记录,以备用时查 1.等值连接和内连接, a.内连接与等值连接效果是相同的,执行效率也相同,只是书写方式不一样,内连接是由SQL 1999规则定的书写方式 比如: sele ...
- Linux的基础知识
什么是操作系统? 操作系统是人与计算机的中介. 操作系统是干什么的? 控制所有资源{硬件资源和软件资源(驱动,应用软件)} 常用的操作系统:Unix Windows Linux Linux的哲学思想: ...