当前课程知识点:操作系统 > 第十二讲 进程控制 > 12.1 进程切换 > 12.1 进程切换
下面我们来介绍进程控制
在进程控制当中呢
我们会涉及这样几个问题
一个是在内核当中的进程切换
也就是说我一个进程
在运行过程当中
内核如何来实现它从一个进程
到另一个进程的切换
后面的三个呢
实际上为用户提供的系统调用服务
那是用户在执行它的应用的过程当中
它有需求要创建一个新的进程
好在这里头呢
我如何来创建一个新的进程
在这里头运行一个新的程序
那这个是进程加载
父进程创建子进程之后
它们俩之间呢
需要有一些协调的关系
你比如说子进程结束之后
父进程负责回收它所占用的资源
那这个时候
它们之间有一个通讯关系
这就是进程的等待和退出
下面我们一个一个的来
首先是进程切换
进程切换呢
有时候也称之为叫上下文切换
它是暂停当前运行进程
把这个进程呢
从运行状态变成其它状态
这里所谓的其它状态呢
可能会是由于进行IO操作
或者等待事件而进入等待状态
也可能是由于被抢先
又或者说时间片运行完
那转回到就绪状态
另一个是说
我把当前的进程停下来之后
那我会调度另一个进程
让它从就绪状态变成运行状态
也就是说我从就绪队列里
找一个新的进程然后恢复
并且让它继续运行
这是进程切换所要做的事情
好那具体说起来
有一些切换的时候
我们有一些什么要求呢
那在切换之前
需要去保存进程的上下文
也就是说
你下一个进程在执行过程当中
需要用到这些CPU的寄存器
状态寄存器
好那这些呢你需要先做保存
好切换到下一个进程运行的时候
在切换过来之后
下一个进程开始执行之前
我还要继续把它上一次
暂停时候的当时的
寄存器的状态恢复回来
好恢复回来之后
那它才能够从
上一次暂停的位置继续往下执行
还有一个要求是说
由于我们现在计算机系统里
进程的切换是非常频繁的
通常情况下是10毫秒左右
就会有一次进程切换
那这样以来的话
为了保证系统运行的效率
这个切换的速度必须非常快
所以通常情况下
它都是由汇编来实现的
那具体说起来我们在这里
要保存一些什么样的信息呢
实际上保存就是进程在整个
生命周期当中它所维护的信息
大致说起来呢
包括这样几个方面
寄存器
那我们在CPU里头通用寄存器
好CPU里头状态信息
然后就内存地址空间里的信息
那你说通常情况下我们
在切换的时候
内存里头的这些信息
是不会被另外一个进程
因为它各占一段区域
是不会被另一个进程所替代的
所以在这里头呢
那内存里的地址
空间内容大部分不用保存
下面我们通过一个图式
来说明进程的切换过程
那在这里头呢
我们假定系统当中有
P0和P1两个进程
它们的执行过程当中呢
先是P0处于用户态执行
那执行过程当中
P1处于空闲状态
那可能会是就绪
也可能会是等待
好那在这里头呢
执行的过程当中
碰着一个系统调用
或者是中断
假定我们这是时钟中断
那它的时间片用完了
到这儿来之后切换到内核
那切换过程当中
它需要保护现场到PCB0
也就进程零的进程控制块里头
把它的当时执行到状态
都保存下来
保存下来之后那这时候说
如果说时钟中断
时间片用完
那这时候我们需要选择
下一个就绪进程
假定这时候
你选择下一个进程是P1
好那到P1需要恢复P1现场
恢复完之后
那这时候呢
状态就已经切到进程P1了
好那这时候P1开始运行
继续运行
好运行到一段时间之后呢
假定说它的时间片又用完了
这时候又产生时钟中断
时钟中断又切回到内核状态
好那这时候它要保存
进程P1的现场
到它对应的进程控制块PCB1
好那在这里头我们假定你又决策
我需要选下一个进程
这时候又选P0
那这时候恢复P0的现场
那这时候从PCB0里头
这两个是对应的
PCB0里头恢复现场
好恢复进程状态之后
它切到进程0
然后继续执行
到这个地方呢
我们一个从进程零切换过去
再切换回来一个完整的过程
就在这儿展现出来了
跟它相关的呢
执行正常执行
然后这个地方呢
是保存现场到PCB0
从PCB0里恢复现场
好中间是PCB1
这边恢复执行到保存
那从这一过程来讲
它是比较清楚了
那这时候我们要在PCB里
记住一些什么信息了
记录进程的运行状态
好那状态呢是放这里头
然后我们把状态呢
它相同的状态
我放到一个队列里头
这时候维护出若干个队列
比如说就绪队列
它有一个起头
然后相应的进程串在一起
那可以用各种各样的办法
来形成这样一个队列
好如果是等待呢
我们就会把它分成
若干种不同类型的等待
比如说在这里头每个设备
等待的设备不同
那它有各自一个队列
比如这里列出来的
磁带01和磁盘0
好然后还有一类呢
就是处于这个
运行到退出的状态的
这时候它的收尾状态
这时候僵尸队列那它对应的
在这里头那处于这样一种状态
这是进程块里头
保存信息和它们如何来组织
好那实际上接下来
我们是说到底会有
一些什么样信息保存到
进程控制块里头呢
那不同的系统里
进程控制块呢是不一样的
比如说在我们 ucore和ucore plus里头
有一个数据结构叫PROC STRUCT
好在这个数据结构里头呢
它保存了进程的相关信息
大致的信息
我们可以把它分成这样几类
一类是进程的标识信息
比如说它的执行
是哪一个可执行文件
它的ID是多少
它的进程ID
好然后它的父进程是谁
好这是一类
然后再一类是进程的状态信息
比如说我们在这里头CPU里头
状态寄存器的相关信息
然后地址空间的起头的位置
第一级页表的起始地址在哪
然后进程的状态和它
是否允许调度等这样一些
执行状态的信息
还有一类呢是进程所占用的资源
比如说它占用的存储资源
那所有分配给
它存储那组织成
相关的数据结构MM
好那上边那个是
它占用的内核堆栈
还有一类呢
是我们保护现场用的
那在中断和进程切换的时候
它都需要保护现场
那这些现场的内容呢
我们刚才说是保存到
进程控制块里头
实际上就是指这个地方
好你两个进程需要复用的部分
就需要在这儿做保存
除此之外呢
这些进程它在
不同的时候处于不同状态
这些状态组成不同队列
那在这里还有几个
相关的指针结构
用于描述我当前进程
到底是在哪一个队列当中
好有了这些信息之后
那我们这个操作系统
就对进程的执行状态呢
有了一个准确的把握了
好我们在ucore里用到
还是比较简单的
对于实际的像Linux windows
那么这时候它的数据结构
比我们要多得多
这是进程控制块的数据结构
在ucore里的完整定义
在我们这里头呢
大家需要注意到的是
我们刚开始说的
这是进程的状态
进程的ID信息
进程ID 线程ID 组ID
然后是进程执行的相关信息
是否需要调度它的父进程是谁
然后这个地方是
进程的内存管理的数据结构
然后下边两个呢
是进程的现场保护的
上下文现场和中断保护现场
然后底下是CR3
CR3呢实际上是页表的起始地址
然后它的标志位
可执行文件的进程的名字
然后是进程的哈希表和链表
那这和我们前面讲的
这里的数据结构呢
是能够完全对得上的
我们在这儿呢是介绍的大致情况
在这里头呢实际上是完整的列表
希望同学下去之后有机会
把这个完整的列表呢
能够有一个基本的了解
好那在这里
我们需要特别说明呢
是内存地址空间的数据结构mm_struct
那在这个数据结构当中呢
我们关心的内容是说
第一个它到底有哪些内存块
内存逻辑地址空间里头
映射是在哪个地方
对应的地址空间是啥样的
好这是第一个
在这里呢
是它的第一级页表的起始地址
pde_t *pgdir
那这是地址空间的起头
然后说如果它有共享的话
那么它又共享了几次
然后如果说在这里头
有跟外存之间的置换
那这时候置换相关数据结构
好有了这些之后
那我们对进程的运行情况就了解了
这是内存管理的内存的数据结构
对应 到代码当中呢
是这个数据结构mm_struct
在这里头我们关注的几个内容呢
是它也会组织成映射的链表
然后这是页表的起始地址的指针
然后这是引用次数
和对mm数据结构进行写的时候
它相应的锁标志信号量
好然后再一个进程里头
ucore里头的进程队列
它怎么来组织呢
那在这里头呢
我们看到基本上是链表
你比如说在这里头
我们有双向 链表
然后说如果说
你这个链表很长
那这时候检索的开销是非常大的
所以在ucore里头又加了hash list
如果说这里链表长的话
先加一级hash队列
然后每一级队列里头呢
hash值相同的
再组成相应的自己的队列
那有了这些办法之后
我们就知道它在这里头
到底它的队列是如何来组织的
好那么具体的在ucore里头
它的一个切换过程是什么样子呢
不管由于什么原因
最后只要你导致切换
都会到schedule内核函数里头来
好到这个函数里头
它干些啥呢
它首先清楚调度标志
那我现在正在调度
不能再进行改调度 标志
然后我这儿呢
查找新的就绪进程
一种可能情况
就是查找完了之后我有可能
还是找回来还是我自己
那这种情况是存在的
好找着一个新的进程
然后说我修改进程的状态标志
那把前头那一个改成
是就绪或者是等待
然后把新那一个
改成是运行状态
好然后接下来进行切换
切换这儿有一个switch_to
这是最后切换的代码
好那在这里头
这个过程大致的流程就有了
好那么实际说起来我们看看这里头
这个图里说的呢
是ucore里头的函数调用关系
那不管是你由于
各种各样的情况出来之后呢
它最后都会到schedule这地方来
比如说在我们这里
是由于退出所过来的情况
好到这个schedule里头来了之后呢
它到这个地方去做选择
和这地方是在做切换
那到这个地方
最后到switch_to这一段汇编呢
它是完成切换的
而准确的切换代码
是什么样的呢
准确的切换代码是跟你平台相关的
每一个CPU平台上
它所需要保存的寄存器是不一样的
而为了保存的速度比较快
恢复速度比较快
那在这里头呢
我们所有切换代码switch_to()
都是用汇编来写成的
那大致的格局呢
是前半段保存切换过去之后
实际上改CR3
改完之后下半段恢复
然后就继续执行了
好这是进程切换的函数调用关系图
那对应到我们实际系统当中呢
这是我们的切换函数它的实现
那我们需要关注的问题
是在于这个地方在切换的时候
它会去改进程的状态
然后选择下一个被调度的进程
从调度队列当中把这个进程取出来
然后后面进行一系列的判断
那我们在这儿呢
这一部分内容呢
我们直接看关注的是它的调用图
那在这儿我们可以看到
schedule()这个函数它调用的地方呢
基本上是我们各种事件出现的时候
你比如说数据发送
然后这个后面会讲到信号量的触发
然后接收到事件
CPU处于空闲状态
发送消息等等一些信息
最后都会导致到我这schedule()
schedule()里呢我们刚才说到proc_run()
这是我们这里关键的函数
到这个地方下来之后有一个
switch_to()
在这个函数里头呢
实际上我们通过跳转
转到我们的汇编代码上了
这个汇编代码呢
就是我们这里的这一段
在这里头呢前面是保存
上一个进程的寄存器的工作
下边是从堆栈当中恢复
进入运行状态的进程的寄存器的值
恢复完毕之后然后返回
那就新的进程就开始继续运行了
那在刚才那个地方呢
switch实际上是通过这里头
这一句C语言的函数
最后跳转到刚才的
这个汇编代码当中来
-0.1 Piazza讨论区
--html
-0.2 在线实验平台
--实验平台使用帮助
--平台使用帮助
-0.2在线实验平台
--Raw HTML
-1.1 课程概述
--视频
-第一讲 操作系统概述--练习
-1.2 教学安排
--视频
-1.3 什么是操作系统
--Video
-1.4 为什么学习操作系统,如何学习操作系统
--Video
-1.5 操作系统实例
--视频
-1.6 操作系统的演变
--视频
-1.7 操作系统结构
--视频
-2.1 前言和国内外现状
-2.2 OS实验目标
-2.3 8个OS实验概述
-2.4 实验环境搭建
-2.5 x86-32硬件介绍
-2.6 ucore部分编程技巧
-2.7 演示实验操作过程
--Q6
--Q7
--Q10
-3.1 BIOS
--3.1 BIOS
-3.2 系统启动流程
-3.3 中断、异常和系统调用比较
-第三讲 启动、中断、异常和系统调用--3.3 中断、异常和系统调用比较
-3.4 系统调用
--3.4 系统调用
-第三讲 启动、中断、异常和系统调用--3.4 系统调用
-3.5 系统调用示例
-3.6 ucore+系统调用代码
-4.1 启动顺序
--4.1 启动顺序
-4.2 C函数调用的实现
-4.3 GCC内联汇编
-4.4 x86中断处理过程
-4.5 练习一
--4.5 练习一
-4.6 练习二
--4.6 练习二
-4.7 练习三
--4.7 练习三
-4.8 练习四 练习五
-4.9 练习六
--4.9 练习六
-5.1 计算机体系结构和内存层次
-5.2 地址空间和地址生成
-5.3 连续内存分配
-5.4 碎片整理
--5.4 碎片整理
-5.5 伙伴系统
--5.5 伙伴系统
-第五讲 物理内存管理: 连续内存分配--5.6 练习
-6.1 非连续内存分配的需求背景
-6.2 段式存储管理
-- 6.2 段式存储管理
-6.3 页式存储管理
-6.4 页表概述
--6.4 页表概述
-6.5 快表和多级页表
-6.6 反置页表
--6.6 反置页表
-6.7 段页式存储管理
-第六讲 物理内存管理: 非连续内存分配--6.8 练习
-7.1 了解x86保护模式中的特权级
-第七讲 实验二 物理内存管理--7.1 了解x86保护模式中的特权级
-7.2 了解特权级切换过程
-第七讲 实验二 物理内存管理--7.2 了解特权级切换过程
-7.3 了解段/页表
-第七讲 实验二 物理内存管理--7.3 了解段/页表
-7.4 了解UCORE建立段/页表
-第七讲 实验二 物理内存管理--7.4 了解UCORE建立段/页表
-7.5 演示lab2实验环节
-8.1 虚拟存储的需求背景
-8.2 覆盖和交换
-8.3 局部性原理
-8.4 虚拟存储概念
-8.5 虚拟页式存储
-8.6 缺页异常
--8.6 缺页异常
-9.1 页面置换算法的概念
-9.2 最优算法、先进先出算法和最近最久未使用算法
-第九讲 页面置换算法--9.2 最优算法、先进先出算法和最近最久未使用算法
-9.3 时钟置换算法和最不常用算法
-第九讲 页面置换算法--9.3 时钟置换算法和最不常用算法
-9.4 Belady现象和局部置换算法比较
-第九讲 页面置换算法--9.4 Belady现象和局部置换算法比较
-9.5 工作集置换算法
-第九讲 页面置换算法--9.5 工作集置换算法
-9.6 缺页率置换算法
-第九讲 页面置换算法--9.6 缺页率置换算法
-9.7 抖动和负载控制
-10.1 实验目标:虚存管理
-第十讲 实验三 虚拟内存管理--10.1 实验目标:虚存管理
-10.2 回顾历史和了解当下
-第十讲 实验三 虚拟内存管理--10.2 回顾历史和了解当下
-10.3 处理流程、关键数据结构和功能
-第十讲 实验三 虚拟内存管理--10.3 处理流程、关键数据结构和功能
-10.4 页访问异常
-第十讲 实验三 虚拟内存管理--10.4 页访问异常
-10.5 页换入换出机制
-第十讲 实验三 虚拟内存管理--10.5 页换入换出机制
-11.1 进程的概念
-第十一讲 进程和线程--11.1 进程的概念
-11.2 进程控制块
-第十一讲 进程和线程--11.2 进程控制块
-11.3 进程状态
-第十一讲 进程和线程--11.3 进程状态
-11.4 三状态进程模型
-11.5 挂起进程模型
-第十一讲 进程和线程--11.5 挂起进程模型
-11.6 线程的概念
-第十一讲 进程和线程--11.6 线程的概念
-11.7 用户线程
-第十一讲 进程和线程--11.7 用户线程
-11.8 内核线程
-第十一讲 进程和线程--11.8 内核线程
-12.1 进程切换
-第十二讲 进程控制--12.1 进程切换
-12.2 进程创建
-第十二讲 进程控制--12.2 进程创建
-12.3 进程加载
-第十二讲 进程控制--12.3 进程加载
-12.4 进程等待与退出
-第十二讲 进程控制--12.4 进程等待与退出
-13.1 总体介绍
-13.2 关键数据结构
-13.3 执行流程
-13.4 实际操作
-14.1 总体介绍
-14.2 进程的内存布局
-14.3 执行ELF格式的二进制代码-do_execve的实现
--14.3 执行ELF格式的二进制代码-do_execve的实现
-14.4 执行ELF格式的二进制代码-load_icode的实现
--14.4 执行ELF格式的二进制代码-load_icode的实现
-14.5 进程复制
-14.6 内存管理的copy-on-write机制
-15.1 处理机调度概念
-第十五讲 处理机调度--15.1 处理机调度概念
-15.2 调度准则
-15.3 先来先服务、短进程优先和最高响应比优先调度算法
--15.3 先来先服务、短进程优先和最高响应比优先调度算法
-第十五讲 处理机调度--15.3 先来先服务、短进程优先和最高响应比优先调度算法
-15.4 时间片轮转、多级反馈队列、公平共享调度算法和ucore调度框架
--15.4 时间片轮转、多级反馈队列、公平共享调度算法和ucore调度框架
-第十五讲 处理机调度--15.4 时间片轮转、多级反馈队列、公平共享调度算法和uc
-15.5 实时调度和多处理器调度
-第十五讲 处理机调度--15.5 实时调度和多处理器调度
-15.6 优先级反置
-第十五讲 处理机调度--15.6 优先级反置
-16.1 总体介绍和调度过程
-16.2 调度算法支撑框架
-16.3 时间片轮转调度算法
-16.4 Stride调度算法
-17.1 背景
--17.1 背景
-17.2 现实生活中的同步问题
-第十七讲 同步互斥--17.2 现实生活中的同步问题
-17.3 临界区和禁用硬件中断同步方法
-第十七讲 同步互斥--17.3 临界区和禁用硬件中断同步方法
-17.4 基于软件的同步方法
-第十七讲 同步互斥--17.4 基于软件的同步方法
-17.5 高级抽象的同步方法
-第十七讲 同步互斥--17.5 高级抽象的同步方法
-18.1 信号量
--18.1 信号量
-第十八讲 信号量与管程--18.1 信号量
-18.2 信号量使用
-第十八讲 信号量与管程--18.2 信号量使用
-18.3 管程
--18.3 管程
-第十八讲 信号量与管程--18.3 管程
-18.4 哲学家就餐问题
-18.5 读者-写者问题
-19.1 总体介绍
-19.2 底层支撑
-第十九讲 实验七 同步互斥--19.2 底层支撑
-19.3 信号量设计实现
-第十九讲 实验七 同步互斥--19.3 信号量设计实现
-19.4 管程和条件变量设计实现
-第十九讲 实验七 同步互斥--19.4 管程和条件变量设计实现
-19.5 哲学家就餐问题
-20.1 死锁概念
-第二十讲 死锁和进程通信--20.1 死锁概念
-20.2 死锁处理方法
-第二十讲 死锁和进程通信--20.2 死锁处理方法
-20.3 银行家算法
-第二十讲 死锁和进程通信--20.3 银行家算法
-20.4 死锁检测
-第二十讲 死锁和进程通信--20.4 死锁检测
-20.5 进程通信概念
-第二十讲 死锁和进程通信--20.5 进程通信概念
-20.6 信号和管道
-第二十讲 死锁和进程通信--20.6 信号和管道
-20.7 消息队列和共享内存
-第二十讲 死锁和进程通信--20.7 消息队列和共享内存
-21.1 文件系统和文件
-第二十一讲 文件系统--21.1 文件系统和文件
-21.2 文件描述符
-第二十一讲 文件系统--21.2 文件描述符
-21.3 目录、文件别名和文件系统种类
-第二十一讲 文件系统--21.3 目录、文件别名和文件系统种类
-21.4 虚拟文件系统
-第二十一讲 文件系统--21.4 虚拟文件系统
-21.5 文件缓存和打开文件
-第二十一讲 文件系统--21.5 文件缓存和打开文件
-21.6 文件分配
-第二十一讲 文件系统--21.6 文件分配
-21.7 空闲空间管理和冗余磁盘阵列RAID
-第二十一讲 文件系统--21.7 空闲空间管理和冗余磁盘阵列RAID
-22.1 总体介绍
-第二十二讲 实验八 文件系统--22.1 总体介绍
-22.2 ucore 文件系统架构
-第二十二讲 实验八 文件系统--22.2 ucore 文件系统架构
-22.3 Simple File System分析
-第二十二讲 实验八 文件系统--22.3 Simple File System分析
-22.4 Virtual File System分析
-第二十二讲 实验八 文件系统--22.4 Virtual File System分
-22.5 I/O设备接口分析
-第二十二讲 实验八 文件系统--22.5 I/O设备接口分析
-22.6 执行流程分析
-23.1 I/O特点
--视频
-第二十三讲 I/O子系统--23.1 I/O特点
-23.2 I/O结构
--816C80A0F5E3B8809C33DC5901307461
-第二十三讲 I/O子系统--23.2 I/O结构
-23.3 I/O数据传输
--C58221E14388B9DB9C33DC5901307461
-第二十三讲 I/O子系统--23.3 I/O数据传输
-23.4 磁盘调度
--567A3F1FCBFB3F4C9C33DC5901307461
-第二十三讲 I/O子系统--23.4 磁盘调度
-23.5 磁盘缓存
--C327536B80D25CE79C33DC5901307461
-第二十三讲 I/O子系统--23.5 磁盘缓存
-html
--html