当前课程知识点:操作系统(RISC-V) >  第十四讲 信号量与管程 >  14.1 信号量 >  14.1 信号量

返回《操作系统(RISC-V)》慕课在线视频课程列表

14.1 信号量在线视频

14.1 信号量

讲义PDF版本参见课程wiki页面:

http://os.cs.tsinghua.edu.cn/oscourse/OS2020spring/lecture14


下一节:14.2 信号量使用

返回《操作系统(RISC-V)》慕课在线视频列表

14.1 信号量课程教案、知识点、字幕

这一讲我们来介绍信号量和管程

信号量和管程

都是操作系统当中

提供的两种同步方法

那最开始我们说

因为我们在系统里呢

为了提高系统的并发性

所以我们引入了多进程和多线程

多线程的引入导致了

资源的共享使用和竞争

我们就会需要去

寻找解决的办法

这就是我们这里要同步

同步呢是为了协调多个进程

对共享数据的访问

我们把这个问题抽象出来

就变成是在任何一个时刻

那我们只能有一个线程

在执行临界区的代码

解决的办法呢

我们前面有这样几种说

我们可以基于底层的硬件实现

也可以在上面呢

有高层的抽象编程方法

那我们今天讲的信号量和管程

都是属于编程方法的两种

这是我们要最终解决的问题

我们需要协调各个线程

对临界资源的访问

然后我们的做法呢有这样几种

首先呢我们在最底层

希望来解决这个问题

你比如说禁止中断

那我也有 做原子操作

把读和修改这两个

变成一个原子操作

比如说我们TS指令

然后我们也有呢

完全基于最基本的读写操作

来构造出来的软件同步办法

这几类办法呢

我们把它都抽象成一个接口

说 就是我们这里前面说的锁

那用锁来申请临界区的进入

和在临界区完成之后那退出释放

好 这个机制能比较好地抽象

而它的实现呢

是依靠这底下这三种办法

这三种办法呢各有各的适用场合

然后有一个统一的锁机制之后

我们在上面能实现临界区的访问

今天我们要说的信号量呢

实际上就是跟锁机制

在同一个层次上的一种编程方法

它的做法是在操作系统的控制下

来做这件事情

信号量是操作系统提供的一种

协调共享资源访问的方法

那这种方法呢

它和我们前面讲到的

软件同步方法有什么区别呢

软件同步方法呢

实际上我们讨论的是平等的

线程之间的一种协调机制

那也就是说我如果用线程

通过软件方法来协调

谁进入临界区的时候

这些线程之间的位置是完全平等的

但是我们这里讲到的信号量呢

实际上它是由操作系统来负责管理的

操作系统作为管理者

它高于我们的进程

这做一个类比

就相当于是说我们在球场上踢球

一种是说两个球队一块踢

没有裁判

好 那这时候

我们有一套共同的规则

好 那这时候大家一起来踢

在这个过程当中呢

如果说有问题 我们照规则来

但实际上这时候呢

大家对规则理解

总是会有各种各样偏差

于是我们引入一个裁判

那裁判就是说

只要我们俩有分歧的时候

找到一个仲裁

由裁判说了算

那我们在这儿呢

实际上就是由操作系统

来做这个裁判

也就说两个进程协调

谁能使用临界区资源

由操作系统说了算

有了一个仲裁者

或者一个管理者之后呢

这种协调会变得比原来容易

好 在这种情况下

我们用一个信号量

来表示一类资源

信号量的取值呢

就是它的资源的数目

这种做法呢是最早

由一个荷兰科学家

叫Dijkstra 他提出的

那这个名字呢

我们在前面的数据结构里头

计算机科学里头

有很多他做出的贡献

这种办法呢在早期的操作系统当中

是最主要的同步机制

那目前呢在我们实际系统当中

这种用的相对来说比较少

但是它的重要性呢仍然是非常高的

它具体怎么做呢

信号量是一种抽象的数据类型

这个数据类型里头有三个内容

一个是一个整型变量

这个变量的取值呢

你可以理解为

你要共享的资源的数目

然后由两个原子操作

那这两个原子操作呢一个叫P操作

它是申请资源的时候要用的

我申请使用一个资源

那我把这个计数呢减一

也就相当于可用的资源数变少了

好 那这时候呢

就可能有一种情况是说

我来申请资源的时候

已经没有资源了

那这时候我就减成一个负数了

减成负数之后呢

我就需要等待另外的线程

用完这个资源之后

它释放了我才能用

好 那这时候呢它就进入等待状态

如果说不是这样 它是大于0的

也就相当于我拿到了相应资源

那我就可以使用了

好 这个P的出处呢是荷兰语

那最早这个科学家他做出来的

我们后续就延续这个提法了

然后反过来呢

我用完资源之后

我释放的时候一个V操作

V操作就是把计数加一

如果说你占用的是

由1变成0的那个资源

后面的线程再来的时候

那它就已经是负数了 它在等待

好 那这时候你加一之后

如果说它仍然是小于等于0的

那么这时候就说明

有另外一个线程

在等这个资源来使用

它目前处于等待状态

好 那这时候呢

我就唤醒相应的等待线程

好 这个V呢

也是跟P一样的

它来自于荷兰语

好 有了这两个之后

我们看这个它怎么来用的

我们做这样一个类比

也就说我假定有一个车站

车站有两个站台

一个站台呢可以停一列火车

进行上下乘客 或者说货物的装卸

好 我们在这儿呢把它抽象成

一个有两个资源的信号量

它做法什么样子呢

在这儿我们看到

这是车站的两个站台

好 没有任何站台被使用的时候

这时候任何一个火车

都是可以进来的

好 那这时候呢有一列火车

直接进到一个站台

然后这时候仍然有资源呢

这时候我这儿还是绿灯

好 等我把这资源占满了

我们通常情况下理解

这时候就有红灯

后面再来火车的话

它就停到这儿了

好 那这时候呢说再来火车

那实际上这时候呢 我就相当于

这地方的P操作减1再减1

这时候由2就减成0了

好 那这时候呢

再来第三辆的时候

它就出去等着了

等第一辆火车装卸完毕 它离开

好 那这时候加1

加1之后发现那

我这里头后面还有一个等着的

那这时候呢它就可以进来了

好 然后这时候呢两个都占用了

那我们在信号灯上看着就是红灯了

那我们实际上信号量的作用呢

就是类似于这个车站的控制流程

好 那我们说信号量它是

由一个整型变量加两个操作

那么这个整型变量呢

加上前面这个操作呢是

P操作和V操作对它进行保护

好这种保护之后呢

实际上我们就能做到

你对信号量中的整型变量的修改

你只能通过PV操作来完成

操作系统呢来保证

这个PV操作的原子性

那实际上这是我们前面说到的

操作系统在执行它的代码的时候

它的优先级高于我们

进程的用户代码

所以在这时候呢

它能保证它在执行过程当中

不受应用进程的代码执行的干扰

好 那这样就能保证它的原子性了

好 那这时候在执行过程当中呢

P操作有可能由于没有资源

而进入等待状态

V操作呢只会释放资源

处于等待状态的进程呢

变成是就绪状态

好 那这时候说

我们用信号量实现的同步

那这时候它能够做到公平吗

在我们实际用的时候呢

通常情况下假设它是公平的

在实际的系统当中呢

这个公平呢也会有些偏差的

那我们在这里说到的公平是指

我线程不会无限期的等待下去

它一定会结束

这是由于操作系统

在这里起的作用

那实际上我们在实际系统用的时候

这个P操作呢它也会说

在后面有一个等的最长时限的参数

那超时之后它直接错误返回

好 然后在这儿信号量的等待队列呢

我们是按先进先出排队

这一条呢实际上

在我们前面说到的锁机制里

自旋锁它实际上是做不到的

为什么呢 原因在于

我们自旋锁是需要占用CPU

随时随地去查

有可能临界区的使用者

退出的时候它刚改完

下一个进入者是谁去查

那它就能进去

如果说你运气不好

正好是这个资源变成有效

你去查的时候在你之前

就有一个人已经查过了

好 那么这时候呢

你就没有办法按照你

等待的这个顺序来做这一条

好 那我们看信号量的实现

我们把它定义成一个数据类型

好那这里头呢有一个变量 sem(semaphore)

然后有一个等待队列q

那这些呢都是在操作系统内核里头的

然后它定义两个操作

P操作和V操作

那在这里干些啥呢

进来之后它是信号量的变量减1

然后 如果你减完之后的值是小于0

那么这时候呢就说明我没有资源了

减到零是可以的

好 那它把这个线程呢

放到等待队列里头

并且呢阻塞

好 那这时候呢切换

好 等到我申请到了

那我就没有进入这个堵塞

我就直接进到临界区里去执行

那执行的过程当中呢

用完了退出的时候作为啥呢

信号量当中的变量加1

好 如果说加一之后还小于0

那就说实际上前面有人在等着

好 那这时候呢

我就从对应的等待队列里头

把相应的线程放到就绪队列里头

这和我们前面讲的

进程的状态的变迁

这样就结合起来了

然后它结束 它就干别的去了

那另一个 就可以进到临界区里头

好 这是我们说到信号量的实现

从这儿来讲好像这个东西很简单

和我们前面区别在哪

就是在于这一段代码

执行的原子性

它是由操作系统来保护的

为啥我们前面做不到这样一点呢

我这里头 减一和判断

那我们在前面

讨论软件方法的时候

最大的问题就在于

我减完一之后和这个判断这俩

中间可能会中断

好 由于这个中断

我们就有无数的麻烦

好 那这个时候现在有

操作系统在里头做保护了

好 我这两个代码

它的执行不会被打断

我们就会发现

这个地方 我变成一个原子之后

我的日子就好过多了

好 那这是我们说

信号量的基本原理

下面我们会去

用实际的例子

来说明它的使用方法

操作系统(RISC-V)课程列表:

第一讲 操作系统概述

-1.1 课程概述

--课程概述

-1.2 教学安排

--视频

-1.3 什么是操作系统

--Video

-1.4 为什么学习操作系统,如何学习操作系统

--Video

-1.5 操作系统实例

--视频

-1.6 操作系统的演变

--视频

-1.7 操作系统结构

--视频

-1.8 OS实验概述

--视频

第二讲 操作系统与系统结构和程序设计语言

-2.1 从OS角度看计算机系统

--2.1 从OS角度看计算机系统

-2.2 从OS角度看RISC-V

--2.2 从OS角度看RISC-V

-2.3 Rust语言与系统编程

--2.3 Rust语言与系统编程

-2.4 RISC-V CPU启动

--2.4 RISC-V CPU启动

-2.5 RISC-V CPU启动进一步分析

--2.5 RISC-V CPU启动进一步分析

第三讲 中断、异常和系统调用

-3.1 基本概念与原理

--3.1 基本概念与原理

-3.2 硬件架构支持

--3.2 硬件架构支持

-3.3 中断处理机制

--3.3.1 中断处理机制–Overview

--3.3.2 中断处理机制–Detail-1

--3.3.3 中断处理机制–Detail-2

--3.3.4 中断处理机制–Detail-3

--3.3.5 中断处理机制–Summary

-3.4 系统调用

--3.4 系统调用

第四讲 物理内存管理: 连续内存分配

-4.1 计算机体系结构和内存层次

--4.1 计算机体系结构和内存层次

-4.2 地址空间和地址生成

--4.2 地址空间和地址生成

-4.3 连续内存分配

--4.3 连续内存分配

-4.4 碎片整理

--4.4 碎片整理

-4.5 伙伴系统

--4.5 伙伴系统

-4.6 SLAB分配器

--4.6 SLAB分配器

第五讲 物理内存管理: 非连续内存分配

-5.1 非连续内存分配的需求背景

--5.1 非连续内存分配的需求背景

-5.2 段式存储管理

-- 5.2 段式存储管理

-5.3 页式存储管理

--5.3 页式存储管理

-5.4 页表概述

--5.4 页表概述

-5.5 快表和多级页表

--5.5 快表和多级页表

-5.6 RISC-V页映射机制

--5.6 RISC-V页映射机制

-5.7 使能RISC-V页表

--5.7 使能RISC-V页表

第六讲 虚拟存储概念

-6.1 虚拟存储的需求背景

--6.1 虚拟存储的需求背景

-6.2 覆盖和交换

--6.2 覆盖和交换

-6.3 局部性原理

--6.3 局部性原理

-6.4 虚拟存储概念

--6.4 虚拟存储概念

-6.5 虚拟页式存储

--6.5 虚拟页式存储

-6.6 缺页异常

--6.6 缺页异常

-6.7 RISC-V缺页异常

--6.7 RISC-V缺页异常

第七讲 虚拟存储:局部页面置换算法

-7.1 页面置换算法的概念

--7.1 页面置换算法的概念

-7.2 最优算法、先进先出算法和最近最久未使用算法

--7.2 最优算法、先进先出算法和最近最久未使用算法

-7.3 时钟置换算法和最不常用算法

--7.3 时钟置换算法和最不常用算法

-7.4 Belady现象和局部置换算法比较

--7.4 Belady现象和局部置换算法比较

-7.5 页表自映射

--7.5 页表自映射

第八讲 虚拟存储:全局页面置换算法

-8.1 工作集置换算法

--8.1 工作集置换算法

-8.2 缺页率置换算法

--8.2 缺页率置换算法

-8.3 抖动和负载控制

--8.3 抖动和负载控制

-8.4 面向缓存的页替换算法

--8.4.1 面向缓存的页替换算法-FBR

--8.4.2 面向缓存的页替换算法-LRU-K 2Q

--8.4.3 面向缓存的页替换算法-LIRS

第九讲 进程和线程

-9.1 进程的概念

--11.1 进程的概念

-9.2 进程控制块

--9.2 进程控制块

-9.3 进程状态

--9.3 进程状态

-9.4 三状态进程模型

--9.4 三状态进程模型

-9.5 挂起进程模型

--9.5 挂起进程模型

-9.6 线程的概念

--9.6 线程的概念

-9.7 用户线程

--9.7 用户线程

-9.8 内核线程

--9.8 内核线程

-9.9 进程地址空间与熔断 (meltdown) 漏洞

--9.9 进程地址空间与熔断 (meltdown) 漏洞

第十讲 进程和线程控制

-10.1 进程切换

--10.1 进程切换

-10.2 进程创建

--10.2 进程创建

-10.3 进程加载

--10.3 进程加载

-10.4 进程等待与退出

--10.4 进程等待与退出

-10.5 rCore进程和线程控制

--10.5 rCore进程和线程控制

第十一讲 处理机调度

-11.1 处理机调度概念

--11.1 处理机调度概念

-11.2 调度准则

--11.2 调度准则

-11.3 先来先服务、短进程优先和最高响应比优先调度算法

--11.3 先来先服务、短进程优先和最高响应比优先调度算法

-11.4 时间片轮转、多级反馈队列、公平共享调度算法和ucore调度框架

--11.4 时间片轮转、多级反馈队列、公平共享调度算法和ucore调度框架

-11.5 实时调度

--11.5 实时调度

-11.6 优先级反置

--11.6 优先级反置

-11.7 rCore调度框架

--11.7 rCore调度框架

第十二讲 多处理机调度

-12.1 对称多处理与多核架构

--12.1 对称多处理与多核架构

-12.2 多处理器调度概述

--12.2 多处理器调度概述

-12.3 O(1)调度

--12.3 O(1)调度

-12.4 CFS调度

--12.4 CFS调度

-12.5 BFS调度算法

--12.5 BFS调度算法

第十三讲 同步互斥

-13.1 背景

--13.1 背景

-13.2 现实生活中的同步问题

--13.2 现实生活中的同步问题

-13.3 临界区和禁用硬件中断同步方法

--13.3 临界区和禁用硬件中断同步方法

-13.4 基于软件的同步方法

--13.4 基于软件的同步方法

-13.5 高级抽象的同步方法

--13.5 高级抽象的同步方法

第十四讲 信号量与管程

-14.1 信号量

--14.1 信号量

-14.2 信号量使用

--14.2 信号量使用

-14.3 管程

--14.3 管程

-14.4 哲学家就餐问题

--14.4 哲学家就餐问题

-14.5 读者-写者问题

--14.5 读者-写者问题

-14.6 Rust语言中的同步机制

--14.6 Rust语言中的同步机制

第十五讲 死锁和并发错误检测

-15.1 死锁概念

--15.1 死锁概念

-15.2 死锁处理方法

--15.2 死锁处理方法

-15.3 银行家算法

--15.3 银行家算法

-15.4 死锁检测

--15.4 死锁检测

-15.5 并发错误检测

--15.5 并发错误检测

第十六讲 进程通信

-16.1 进程通信概念

--16.1 进程通信概念

-16.2 信号和管道

--16.2 信号和管道

-16.3 Linux信号机制

--16.3 Linux信号机制

-16.4 消息队列和共享内存

--16.4 消息队列和共享内存

-16.5 D-Bus机制

--16.5 D-Bus机制

-16.6 Binder机制

--16.6 Binder机制

第十七讲 文件系统概念

-17.1 文件系统和文件

--17.1 文件系统和文件

-17.2 文件描述符

--17.2 文件描述符

-17.3 目录、文件别名和文件系统种类

--17.3 目录、文件别名和文件系统种类

-17.4 虚拟文件系统

--17.4 虚拟文件系统

-17.5 文件缓存和打开文件

--17.5 文件缓存和打开文件

-17.6 文件分配

--17.6 文件分配

-17.7 空闲空间管理和冗余磁盘阵列RAID

--17.7 空闲空间管理和冗余磁盘阵列RAID

第十八讲 文件系统实例

-18.1 FAT文件系统

--18.1 FAT文件系统

-18.2.1 EXT4文件系统-历史

--18.2.1 EXT4文件系统-历史

-18.2.2 EXT4文件系统-支持大容量存储

--18.2.2 EXT4文件系统-支持大容量存储

-18.2.3 EXT4文件系统-支持恢复异常

--18.2.3 EXT4文件系统-支持恢复异常

-18.3 ZFS文件系统

--18.3 ZFS文件系统

第十九讲 I/O子系统

-19.1 I/O特点

--19.1 I/O特点

-19.2 I/O结构

--19.2 I/O结构

-19.3 I/O数据传输

--19.3 I/O数据传输

-19.4 磁盘调度

--19.4 磁盘调度

-19.5 Linux I/O子系统

--19.5 Linux I/O子系统

第二十讲 内核与程序设计语言

-20.1 Linux内核错误分析

--20.1 Linux内核错误分析

-20.2.1 用rust写操作系统-系统编程语言rust

--20.2.1 用rust写操作系统-系统编程语言rust

-20.2.2 用rust写操作系统-rust与操作系统开发

--20.2.2 用rust写操作系统-rust与操作系统开发

第二十一讲 异步编程 (Asynchronous Programming)

-21.1 Background

--21.1 Background

-21.2 Futures in Rust

--21.2 Futures in Rust

-21.3 Generators and async/await

--21.3 Generators and async/await

-21.4 Self-Referential Structs & Pin

--21.4 Self-Referential Structs & Pin

-21.5 Waker and Reactor

--21.5 Waker and Reactor

第二十二讲 Virtual Machine Monitor

-22.1 Overview

--22.1 Overview

-22.2.1 How VMM works - CPU

--22.2.1 How VMM works - CPU

-22.2.2 How VMM works - memory & I/O

--22.2.2 How VMM works - memory & I/O

14.1 信号量笔记与讨论

也许你还感兴趣的课程:

© 柠檬大学-慕课导航 课程版权归原始院校所有,
本网站仅通过互联网进行慕课课程索引,不提供在线课程学习和视频,请同学们点击报名到课程提供网站进行学习。