当前课程知识点:操作系统 > 第十一讲 进程和线程 > 11.6 线程的概念 > 11.6 线程的概念
下面我们来讨论线程
在刚才我们讨论进程的时候呢
每个进程内部它的指令执行呢
是有一个叫指令指针的寄存器来描述
当前这个进程执行到什么地方
但是在我们实际使用的时候呢
一个进程内部我也可能会希望
它有更好的并发性
那这就是我们在这里
引入线程的原因
那在这儿我们先讨论
引入线程我们通过一个实例来说
为什么我们要在一个进程内部
再进一步提高它的并发性
好 然后说什么是一个线程
在这儿呢我们给入这样一个例子
说我有一个应用
这个应用呢是播放音频的 MP3
那它的功能呢包括这样几个方面
一个呢是说我从音频文件里头
读出相应的数据
然后通常情况下
我们的音频文件是压缩的
然后我需要对数据进行解压
解压之后呢
我送到声卡去进行播放
这是它要进行的三步
这三步呢我们怎么来实现呢
这个地方给出了一种
用单进程的方式来实现的
也就是说我在这个进程内部呢
做了一个循环
这个循环里头呢读数据
解压然后播放一步一步来
这个程序能很好的工作吗
那你说我在这里头
读磁盘足够速度快
然后解压足够速度快
那我解压完了之后给这边播放
播放还没有播放完之前
我下一部分的解压数据
能跟得上的话
这个程序是可以很好工作的
但是如果说你细看下去的话
这个程序会在什么地方有问题呢
第一部分它需要
频繁的进行IO操作 进行读写
而第二部分呢
解压它主要是用CPU来算
这两个部分呢是交替进行的
实际上我们在系统里呢
这两个部分是
应该可以并发进行的
但实际上在这儿呢这件事情
做起来就比较困难了
如果说我在负载比较重的时候
或者说我的处理能力比较弱的时候
那这时候呢
播放声音的连贯性就会受到影响
然后它资源的利用效率呢
也会比较低
好 因为这样的话
我就没有办法干别的事情了
因为要不然的话稍微慢一点
好 最后我播出来的声音
也就是说在声音上一次给的
数据缓冲没播完之前
如果说你下一波数据没过来的话
那这地方声音它就会不连贯
那说这样播不好
那我们是不是可以把这个程序改成
由多个进程来一块写呢
好 那这时候说我把它改成
三个进程来实现
第一个进程是
管读数据读到内存里头
第二个进程呢是管解压缩
第三个进程管播放
那第一个读完数据的时候给第二个
第二个解压的过程中
由于操作系统的并发性
我可以切换运行
好 那这几个呢就可以
在你读数据的等待阶段
我就可以进行解压了
好 这样的话说起来
它们实际上可以并行
但这时候呢
又有一个问题说
我在这个地方
在做这件事情的时候
它们之间如何来通讯
如何来共享数据
那我们在前面大家还记得
我们在讨论进程的时候
我们讨论进程控制块
主要的一个目的是说让
各个进程之间能很好进行隔离
而现在呢我们这几个隔离
不是它最主要的要求
它们需要的要求是
它们之间能更好共享数据
而这个要求和我们最开始设计进程
多进程的时候
它的目标呢是有一定矛盾的
好 那在这种情况下
我们 它的开销也是相对较大的
因为两个进程之间进行通讯
通常情况下都通过
系统调用要从内核里绕一圈
那如果说这些都是在一个进程内部
那这件事情就会好很多
正是由于这种缘故
我们希望在这里呢
得到这样一种对进程进行改进
在进程内部增加一类实体
使得这一类实体呢
在一个进程内部呢它可以并发执行
有多个指令指针在执行
同时呢它们之间共享的相同地址空间
这样呢它们的信息交流呢
会比原来更方便
因为这时候隔离
不是它们之间要解决的问题
因为它是密切相关的一组执行流
好 而正是由于这种需求
就导致了我们线程的引入
好 那这种思路目标我已经有了
那我怎么来做呢
这就是我们这里说的线程的概念
线程是进程的一部分
它描述指令执行流的状态
好 那它在这里头我把进程当中的
关于执行流的信息呢剥离出来
构成我的线程
但它仍然是进程的一部分
这种剥离有什么样的好处呢
剥离它就可以为并发
在一个进程里头
有多个线程啊提供可能
好 那这样的话把指令执行流的
最小单位变成是线程
然后CPU调度呢变成是线程
好 有了这两条之后
那我在进程内部的并发呢
就变为可能了
好 有了这种变化之后
我们原来的进程控制块
会是啥变化呢
好 原来的进程呢
变成是资源分配的角色
那这是我们的地址空间
我们大家可以看到的
在这里头跟我们原来比的话
跟指令流相关东西就不放在进程里头了
那这里头跟它相关什么
就是堆栈
每个指令流它有函数调用的时候
它必须有自己独立的堆栈
好 那这样我把它剥离出来
变成是我们这里的线程的组成部分
那线程呢是来负责处理机调度的对象
好 那么在这儿呢 我有若干个线程
我在这里有各自线程 有自己堆栈
好 那把相关的关于执行流的状态
的信息呢变成是线程控制块
好 那这个线程控制块呢
也从属于我们的进程控制块
用指针指向它
好 那这时候我就可以有多个指令指针
多个堆栈
和多个CPU里的寄存器的现场保护
因为这个现场保护是和执行流相关的
好 有了这样一个概念之后呢
我们就可以在一个进程内部呢
提高它的并发程度
这个道理说完之后呢
它大致想起来是可以的
那这时候我们会做一些什么样的变化
好 这是我们原来描述的进程的信息
有代码数据 文件 寄存器 堆栈
这是我们原来有的
那在原来里头我只有一个指令指针
所以这部分只有一份
我改完之后变成什么样子呢
这是多线程的进程 那在这里头呢
代码数据打开文件
仍然是和进程属性相关的
它是进程的属性
而跟执行流相关的
我可以变成多份
这时候呢寄存器堆栈
变成是我们线程的属性
线程是进程里的一部分
这时候这样做变化之后
我就可以有多个线程在一个进程内存在了
好 那有了这样做之后呢
我们从道理上来讲我们多线程就可以
很好的支持进程内部的并发
好 这是我们说线程是进程减去资源共享
剩下部分那就是我们执行流的信息
好 那这样的话
我们这样做完之后
线程的引入有什么样好处呢
一个进程内有多个线程
线程之间呢可以并发执行
线程之间呢
可以共享地址空间和文件等资源
这样共享会变得方便
并发性会变得好
那它有啥麻烦呢
因为你在一个进程内部的多个指令流
那这时候呢它们之间的隔离就没有了
好 如果说一个线程行为有异常
它改了另一个线程的相关信息
那么这时候你的整个进程地址空间里的
这些线程就都运行不了了
好 这是呢但是说这种缺点呢是说
我们不是重点关注的
并且我认为各个线程之间
是相互合作的
所以它隔离的成分
我们不要求那么长
好 那有了这样之后我们来说
现在我们在历史上这个变化的过程
这个多线程思路是很好的
那我们看一下这个思路
它在历史上出来的时候
它会是怎样一个变化的过程
也就是说我们现在这些系统里
是如何逐步来支持这个多线程的
最早的计算机系统呢
里头只有一个物理的
指令指针寄存器
那这就是我们最早的单进程系统
比如说像我们的MS-DOS整个系统里
只有一个指令指针流在跑
也就相当于这时候
我们关于进程管理这些机制呢
这里头都没有
好 然后我们说希望提高它的并发性
好 这时候加了多进程系统
那这时候呢我们传统的UNIX系统
就是从这么做起来的
这时候我们在进程切换
和进程之间的资源隔离
那都是在这个阶段给出来的
那有了这个阶段之后
我们想进一步提高
这个一个进程内部的并发性
就变成了我们现在的多线程系统
那在一个进程
同时有多线程和多进程
那就是我们现在的
这种系统里呢支持的情况
实际上在这里头我们还有一类系统
在现实的生活当中也是存在的
这就是单进程多线程系统
那这一些系统呢
通常情况下是对于
那些对并发执行要求高
对信息共享要求高的
但是对安全隔离
要求低的这些系统里头
比如说像我们的路由器里头
路由器里头呢
它是负责分组的转发
路由表的查询
这些是密切相关的
但是它们之间呢
需要很好的这个信息共享
但是由于他们都是一家做的
并且路由器里呢不支持
你第三方往顶上加自己的程序
好 那这时候呢它的隔离
就做的比较弱
好 那这样的话不做隔离的结果就是
我这里只支持一个进程里头
有很多的线程
比如说像我们用到的PSOS操作系统
它在路由器上用的它就有这种特征
好 那有了这个之后 那我们来看
进程和线程
它们实际上什么样的关系呢
进程是资源分配的单位
线程是处理机调度的单位
好 进程和线程呢
一个是拥有完整的资源
一个是只拥有跟执行流相关的资源
那这样一来的话我们线程呢
它所要保存信息就少了
那在同一个进程里头
它们相互之间切换的时候
你需要保存的信息就少了
这个时候它的速度就会快
好 每一个原来说跟执行相关的
这些进程状态
现在都转成是线程的状态了
所以线程有就绪 等待 执行 运行
几种基本的状态
它们之间的状态的转换
跟我们前面讲的进程呢是完全一致的
好 有了这些之后
那我们的线程呢
就会有很好的特征
那这时候它减少
并发执行的时间和空间开销
时间开销呢是指说创建终止切换
它的时间都会比原来少
这里所说的创建终止切换
是指在一个进程内部的线程的创建
如果说你创建一个
新的进程里的新线程
那和原来的进程创建呢
开销是相当的
甚至于有可能会多一点
好 然后说我在这里头呢
它们之间的共享呢
会比原来更方便
好 那到这个地方呢
我们就说清楚了线程的基本概念
那下面我们会去说如何来实现它
-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