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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux 进程管理之进程调度与切换相关的知识,希望对你有一定的参考价值。

参考技术A

我们知道,进程运行需要各种各样的系统资源,如内存、文件、打印机和最

宝贵的 CPU 等,所以说,调度的实质就是资源的分配。系统通过不同的调度算法(Scheduling Algorithm)来实现这种资源的分配。通常来说,选择什么样的调度算法取决于资源分配的策略(Scheduling Policy)。

有关调度相关的结构保存在 task_struct 中,如下:

active_mm 是为内核线程而引入的,因为内核线程没有自己的地址空间,为了让内核线程与普通进程具有统一的上下文切换方式,当内核线程进行上下文切换时,让切换进来的线程的 active_mm 指向刚被调度出去的进程的 active_mm(如果进程的mm 域不为空,则其 active_mm 域与 mm 域相同)。

在 linux 2.6 中 sched_class 表示该进程所属的调度器类有3种:

进程的调度策略有5种,用户可以调用调度器里不同的调度策略:

在每个 CPU 中都有一个自身的运行队列 rq,每个活动进程只出现在一个运行队列中,在多个 CPU 上同时运行一个进程是不可能的。

运行队列是使用如下结构实现的:

tast 作为调度实体加入到 CPU 中的调度队列中。

系统中所有的运行队列都在 runqueues 数组中,该数组的每个元素分别对应于系统中的一个 CPU。在单处理器系统中,由于只需要一个就绪队列,因此数组只有一个元素。

内核也定义了一下便利的宏,其含义很明显。

Linux、c/c++服务器开发篇-------我们来聊聊进程的那些事

Linux内核 进程间通信组件的实现

学习地址:C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂

需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括 C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg 等),免费分享

在分析调度流程之前,我们先来看在什么情况下要执行调度程序,我们把这种情况叫做调度时机。

Linux 调度时机主要有。

时机1,进程要调用 sleep() 或 exit() 等函数进行状态转换,这些函数会主动调用调度程序进行进程调度。

时机2,由于进程的时间片是由时钟中断来更新的,因此,这种情况和时机4 是一样的。

时机3,当设备驱动程序执行长而重复的任务时,直接调用调度程序。在每次反复循环中,驱动程序都检查 need_resched 的值,如果必要,则调用调度程序 schedule() 主动放弃 CPU。

时机4 , 如前所述, 不管是从中断、异常还是系统调用返回, 最终都调用 ret_from_sys_call(),由这个函数进行调度标志的检测,如果必要,则调用调用调度程序。那么,为什么从系统调用返回时要调用调度程序呢?这当然是从效率考虑。从系统调用返回意味着要离开内核态而返回到用户态,而状态的转换要花费一定的时间,因此,在返回到用户态前,系统把在内核态该处理的事全部做完。

Linux 的调度程序是一个叫 Schedule() 的函数,这个函数来决定是否要进行进程的切换,如果要切换的话,切换到哪个进程等。

从代码分析来看,Schedule 主要完成了2个功能:

进程上下文切换包括进程的地址空间的切换和执行环境的切换。

对于 switch_mm 处理,关键的一步就是它将新进程页面目录的起始物理地址装入到寄存器 CR3 中。CR3 寄存器总是指向当前进程的页面目录。

switch_to 把寄存器中的值比如esp等存放到进程thread结构中,保存现场一边后续恢复,同时调用 __switch_to 完成了堆栈的切换。

在进程的 task_struct 结构中有个重要的成分 thread,它本身是一个数据结构 thread_struct, 里面记录着进程在切换时的(系统空间)堆栈指针,取指令地址(也就是“返回地址”)等关键性的信息。

关于__switch_to 的工作就是处理 TSS (任务状态段)。

TSS 全称task state segment,是指在操作系统进程管理的过程中,任务(进程)切换时的任务现场信息。

linux 为每一个 CPU 提供一个 TSS 段,并且在 TR 寄存器中保存该段。

linux 中之所以为每一个 CPU 提供一个 TSS 段,而不是为每个进程提供一个TSS 段,主要原因是 TR 寄存器永远指向它,在任务切换的适合不必切换 TR 寄存器,从而减小开销。

在从用户态切换到内核态时,可以通过获取 TSS 段中的 esp0 来获取当前进程的内核栈 栈顶指针,从而可以保存用户态的 cs,esp,eip 等上下文。

TSS 在任务切换过程中起着重要作用,通过它实现任务的挂起和恢复。所谓任务切换是指,挂起当前正在执行的任务,恢复或启动另一任务的执行。

在任务切换过程中,首先,处理器中各寄存器的当前值被自动保存到 TR(任务寄存器)所指定的任务的 TSS 中;然后,下一任务的 TSS 被装入 TR;最后,从 TR 所指定的 TSS 中取出各寄存器的值送到处理器的各寄存器中。由此可见,通过在 TSS 中保存任务现场各寄存器状态的完整映象,实现任务的切换。

因此,__switch_to 核心内容就是将 TSS 中的内核空间(0级)堆栈指针换成 next->esp0。这是因为 CPU 在穿越中断门或者陷阱门时要根据新的运行级别从TSS中取得进程在系统空间的堆栈指针。

thread_struct.esp0 指向进程的系统空间堆栈的顶端。当一个进程被调度运行时,内核会将这个变量写入 TSS 的 esp0 字段,表示这个进程进入0级运行时其堆栈的位置。换句话说,进程的 thread_struct 结构中的 esp0 保存着其系统空间堆栈指针。当进程穿过中断门、陷阱门或者调用门进入系统空间时,处理器会从这里恢复期系统空间栈。

由于栈中变量的访问依赖的是段、页、和 esp、ebp 等这些寄存器,所以当段、页、寄存器切换完以后,栈中的变量就可以被访问了。

因此 switch_to 完成了进程堆栈的切换,由于被切进的进程各个寄存器的信息已完成切换,因此 next 进程得以执行指令运行。

由于 A 进程在调用 switch_to 完成了与 B 进程堆栈的切换,也即是寄存器中的值都是 B 的,所以 A 进程在 switch_to 执行完后,A停止运行,B开始运行,当过一段时间又把 A 进程切进去后,A 开始从switch_to 后面的代码开始执行。

schedule 的调用流程如下:





Linux进程管理与调度-之-目录导航

转自:http://blog.csdn.net/gatieme/article/details/51456569

 
 
日期内核版本架构作者GitHubCSDN
2016-07-21 Linux-4.6 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度

1 项目链接


项目描述
KernelInKernel 一个运行在linux上的小巧内核, 修改了linux-kernel的start_kernel以启动我们自己的内核, 基于jserv/kernel-in-kernel(基于linux-4.1.0)和mengning/mykernel(基于linux-3.9.4), 适合学习和研究调度算法
Linux进程管理与调度 CSDN博客–Linux进程管理与调度
LDD-LinuxDeviceDrivers 与CSDN博客同步更新, 但是除了包含博客的内容, 还包含了一些以驱动方式实现的实验代码

2 进程的描述


CSDNGitHub
Linux进程描述符task_struct结构体详解–Linux进程的管理与调度(一) study/kernel/01-process/01-task/01-task_struct
Linux的命名空间详解–Linux进程的管理与调度(二) study/kernel/01-process/01-task/02-namespace
Linux进程ID号–Linux进程的管理与调度(三) study/kernel/01-process/01-task/03-pid

3 进程的创建


CSDNGitHub
Linux下的进程类别(内核线程、轻量级进程和用户进程)以及其创建方式–Linux进程的管理与调度(四) study/kernel/01-process/02-create/01-duplicate
Linux下0号进程的前世(init_task进程)今生(idle进程)—-Linux进程的管理与调度(五) study/kernel/01-process/02-create/02-idel
Linux下1号进程的前世(kernel_init)今生(init进程)—-Linux进程的管理与调度(六) study/kernel/01-process/02-create/03-init
Linux下2号进程的kthreadd–Linux进程的管理与调度(七) study/kernel/01-process/02-create/04-kthreadd
Linux下进程的创建过程分析(_do_fork/do_fork详解)–Linux进程的管理与调度(八) study/kernel/01-process/02-create/05-do_fork
Linux进程内核栈与thread_info结构详解–Linux进程的管理与调度(九) study/kernel/01-process/02-create/06-thread_info
Linux内核线程kernel thread详解–Linux进程的管理与调度(十) study/kernel/01-process/02-create/07-kernel_thead

4 进程的加载与运行


CSDNGitHub
Linux进程启动过程分析do_execve(可执行程序的加载和运行)—Linux进程的管理与调度(十一) study/kernel/01-process/03-execute/01-do_execve
LinuxELF文件格式详解–Linux进程的管理与调度(十二) study/kernel/01-process/03-execute/02-elf
ELF文件的加载过程(load_elf_binary函数详解)–Linux进程的管理与调度(十三) study/kernel/01-process/03-execute/03-load_elf_binary

5 进程的退出


CSDNGitHub
Linux进程退出详解(do_exit)–Linux进程的管理与调度(十四)) study/kernel/01-process/04-exit/01-do_exit

6 进程的调度


CSDNGitHub
Linux进程调度器概述–Linux进程的管理与调度(十五) study/kernel/01-process/05-schedule/01-introduction
Linux进程调度策略的发展和演变–Linux进程的管理与调度(十六) study/kernel/01-process/05-schedule/02-develop
Linux进程调度器的设计–Linux进程的管理与调度(十七) study/kernel/01-process/05-schedule/03-design
Linux核心调度器之周期性调度器scheduler_tick–Linux进程的管理与调度(十八) study/kernel/01-process/05-schedule/03-design/02-periodic_scheduler
Linux进程核心调度器之主调度器–Linux进程的管理与调度(十九) study/kernel/01-process/05-schedule/03-design/03-main_scheduler
Linux用户抢占和内核抢占详解(概念, 实现和触发时机)–Linux进程的管理与调度(二十) study/kernel/01-process/05-schedule/03-design/04-preempt
Linux进程上下文切换过程context_switch详解–Linux进程的管理与调度(二十一) study/kernel/01-process/05-schedule/03-design/05-context_switch
Linux进程优先级的处理–Linux进程的管理与调度(二十二) study/kernel/01-process/05-schedule/03-design/06-priority
Linux唤醒抢占—-Linux进程的管理与调度(二十三) study/kernel/01-process/05-schedule/03-design/07-wakeup

7 调度普通进程-完全公平调度器CFS


CSDNGitHub
Linux进程调度之CFS调度器概述–Linux进程的管理与调度(二十四) study/kernel/01-process/05-schedule/07-cfs/01-cfs/
Linux CFS调度器之负荷权重load_weight–Linux进程的管理与调度(二十五) study/kernel/01-process/05-schedule/07-cfs/02-load_weight/
Linux CFS调度器之虚拟时钟vruntime与调度延迟–Linux进程的管理与调度(二十六) study/kernel/01-process/05-schedule/07-cfs/03-vruntime/
Linux CFS调度器之队列操作–Linux进程的管理与调度(二十七) study/kernel/01-process/05-schedule/07-cfs/04-queue/
Linux CFS调度器之pick_next_task_fair选择下一个被调度的进程–Linux进程的管理与调度(二十八) study/kernel/01-process/05-schedule/07-cfs/05-pick_next/
Linux CFS调度器之task_tick_fair处理周期性调度器–Linux进程的管理与调度(二十九) study/kernel/01-process/05-schedule/07-cfs/06-task_tick_fair/
Linux CFS调度器之唤醒抢占–Linux进程的管理与调度(三十) study/kernel/01-process/05-schedule/07-cfs/07-task_new_fair/

以上是关于Linux 进程管理之进程调度与切换的主要内容,如果未能解决你的问题,请参考以下文章

Linux进程上下文切换过程context_switch详解--Linux进程的管理与调度(二十一)

Linux进程管理与调度-之-目录导航

Linux进程调度器概述--Linux进程的管理与调度(十五)

Linux性能及调优指南(翻译)之Linux进程管理

Linux CFS调度器之唤醒抢占--Linux进程的管理与调度(三十)

Linux进程描述符task_struct结构体详解--Linux进程的管理与调度