当前课程知识点:操作系统 > 第十七讲 同步互斥 > 17.5 高级抽象的同步方法 > 17.5 高级抽象的同步方法
下面我们来介绍第三类同步方法
就是这里的更高级的抽象方法
它实际上是基于硬件的同步原语
来实现的同步方法
我们在前面已经说过中断禁用
可以实现进程之间的同步
那还有一类呢是原子操作指令
也就说把若干个我们在同步当中
会用到的操作
把它合成一个原子操作
在硬件上保证它的原子性
这样的话 操作系统
就可以基于这个来实现
更高级的编程抽象
简化进程同步
第一个呢是锁
锁呢实际上是一个抽象的数据结构
这个数据结构呢
由一个二进制变量
和两个操作原语组成
二进制变量呢它表示两种状态
一种是锁定一种是解锁
而两个操作呢是这样的
一个是锁的请求
我要申请这个锁
那这个操作呢 它是原子的
在请求的过程当中
一直到获取到这个锁之前
它都一直处于等待状态
那它返回的时候
你就已经获得这个锁了
第二个操作呢是释放
释放锁呢 它是唤醒其它的
等待这个锁的进程
有了这两个操作之后
我们用它来实现临界区的访问
那就很方便了
那这是临界区的访问
进入区就是请求操作
退出区呢就是释放操作
那有了这个之后这件事情就行了
那它内部呢我们是
基于原子操作指令
原子操作指令呢
是CPU体系结构当中
提供的一类特殊的指令
这些指令呢把若干个操作
合成一个原子操作
保证它们之间中间
不会出现部分执行的状态
一个呢测试与置位指令
通常我们说的TS指令
它的功能呢
是从内存单元中读取数据
测试并且置位该存储单元
测试呢就是判断这个值是不是1
然后并且往这个单元里呢写1
也就说如果里头是1
那你读出来写进去的还是1
内容没变
但是我们知道里头是1了
好 如果里头是0
那这时候呢我读出来的0
写进去的1
好 那这是我对它进行修改了
这条指令的准确含义呢
可以用这个伪码来表示
实际上这是从里头读出一个量来
然后把另外一个量写进去
并且把读出的量呢返回回来
这是它的功能
这三个基本的操作合到一起
它是一个原子操作
它们不会被中断
好 基于这一条 我们来实现
我们前面的同步就会很方便
另一条呢是交换指令
它的功能呢是交换内存中两个值
实际上它的指令含义呢
我们可以用这样一段
伪码来表示
用一个temp
把第一个值读出来
然后把第二个值复制过去之后
再把你读出来的值呢
复制到第二个值
好 用这种办法呢
我们可以把两个值呢做交换
这也是一个原子操作
也就说读出来和写进去之间
不会产生部分执行的状态
那如果说我们
以前面的分配进程ID为例
如果我们这里读出来和
写进去这两个是一个整体的话
那我ID加1就没问题了
好 这是两个基本的原语
基于这两个原子操作指令
我们可以来实现锁
那在这儿呢 我们先是
用TS指令来实现自旋锁
它的做法是这样的
初始化
你这个锁里这个初值为0
然后我来构造它的请求操作原语
实际上这里头呢
就是用TS指令去把
你这里的value这个值读出来
并且往里写1
那如果里头是1
那这个操作呢就相当于是个检查
如果里头是0
那么就把里设置为1
并且这个循环结束
好 它就进到下面这个页区去了
好 释放的时候呢
就是把这个值改成0
那我们看这个写入的过程呢
我们把它展开之后
变成这样两种
一种呢原来里头是0
那么这时候呢
实际上相当于这个锁
处于解锁的状态
好 这时候我把里写成1
也就相当于我占用这个锁
并且使得其它的进程就处于
后续来的就处于等待状态了
而如果锁处于锁定的状态
也就处于忙的状态
那么这时候呢你读出来的是1
写进去的是1
好 那它的状态
没有发生任何的改变
好 那这是用TS指令
实现的自旋锁
那我们看看它有什么样的特点
那在这里头呢是
这个自旋锁里头的TS指令的执行
它是要耗用CPU时间的
所以它在等待状态的时候呢
它是消耗CPU的
好 那我们看基于这个
我是不是可以把等待的时候
占用CPU的这件事情呢有所缓解
那我们就把这个
变成一个无忙等待的锁
那怎么做呢
首先我在你那个锁的数据结构里呢
加上一个等待队列
这个等待队列呢
就是我等待这个锁的
相应的进程所排的队列
然后我把这个锁的请求操作
在里又增加一段内容
先while 进行判断
如果说在这里头呢
查询一次之后 里头是1
那么我就把我当前线程呢
放到等待队列里头
同时呢 执行调度程序
那这时候其它进行可以继续执行
好 一旦是切换回来
轮着我再运行的时候
就是有释放锁操作
把我这个线程从等待状态
变成就绪状态了
好 回来的时候呢我再继续去查
好 如果这时候
它的状态是解锁的状态
那请求就完成了
好 这时候我就可以进入到临界区
好 转过身来在释放的时候
比我们刚才的忙等待多了点什么
填0 表示释放锁
这跟原来是一样的
那加了一句就是
我把这个线程呢
从等待队列里头
放到就绪队列里头去
这是这一步做的
好 有了这个的话
那你在等待的过程当中呢
我就处于放弃CPU使用权的状态
这样的话我CPU就可以干别的了
好 我们看到在这个过程当中呢
我们就实现了让权等待
这是基于TS指令的实现
那我们说基于交换指令
也是一样可以来做成这件事情的
那基本上就是把这个
TS指令的功能换成交换指令
好 基于原子操作指令
所构造的这种锁
它有什么样的特征呢
它是适应于单处理机
也适应于共享内存的多处理机
我们前面讲的中断禁用
只能用于单处理机
如果我有多个处理机的话
你在一个处理机上
禁止中断的响应 那不管用
另一个处理机上如果说有中断响应
或者说有其它的进程执行
那它仍然可以修改
你共享的那些变量
好 再有一个呢
它能适应任意数目的进程同步
那在这里头它不像我们前面
你两个进程同步是一种状态
三个更多的时候
这种状态就会更复杂
那么在这儿呢都是一样
相对来说它是简单
并且容易验证它的正确性
好 它支持多临界区
如果你有若干个临界区
每一个临界区对应着
一个锁那就可以了
当然它也有问题
如果说你是忙等待锁
那这时候呢它会占用CPU的时间
另外呢它可能导致饥饿
也就相当于我在这里呢
并没有做到
按先来后到的顺序来使用资源
因为我在锁的请求操作当中呢
我放到就绪队列里头来之后
我会再去检查
就像我们前面说的
你在检查那个时刻
如果说资源处于空闲状态
那就申请到了
那你回来的时候呢
实际上各个线程它把就绪队列
排定的顺序并不见得是
我们申请锁的这个顺序
同时它还可能出现死锁
那这描述一种死锁的情况是
有一个低优先级的进程
它占用了临界区资源
好 另一个请求
访问临界区的高优先级进程
由于它优先级高
那么这时候呢
它就可以占用CPU来执行
而占用CPU来执行呢
实际上它执行的那个代码是请求
如果那个地方你用的是忙等待
那么这时候呢它就一直等待
这样的话两边就相互等了
低优先级等CPU
高优先级等临界区资源
好 那这两个互不相让
那我们就构成死锁了
好 这是基于硬件同步操作指令
来实现的同步办法
好 它可以有很好的性能
有很好的特征
但它需要硬件的支持
好 到这个地方呢
我们就介绍完了
这三种同步的方法
那禁用中断呢
它比较简单 然后
它仅适用于单处理机
并且它会对
系统中断的响应时间呢会有影响
那通常情况下
我们只是在不得已的时候
才会去用它
也就说我没有其它办法的时候
我才会去用它
第二种呢是软件办法
它依赖的条件会比较弱
但是它的实现非常复杂
那在我们现在这里头呢
用的最多的呢
是基于原子操作指令
实现的同步的方法
那它不仅适用于单处理机
也适用于多处理机
具有容易验证的特征
好 这是我们介绍的三种同步方法
好 今天的课就上到这里 下课
-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