清华大学操作系统网课笔记
课程地址
操作系统将CPU抽象成进程,将磁盘抽象成文件,将内存抽象成地址空间
并发和并行的区别:并发是一段时间内有多个程序可以运行,并行是一个时间点上有多个程序可以执行
内核态和用户态:
用户态是应用程序执行过程中CPU所处于的执行的特权级状态,特权级比较低
内核态是操作系同运行过程中CPU所处于的状态,这个状态中可以执行特权指令
系统调用:应用程序请求操作系统提供服务
当应用程序调用系统调用时会完成从用户态到内核态的装换,从而让控制权从应用程序转到操作系统
系统调用和函数调用的区别:
函数调用是在一个栈空间,而系统调用是使用不同的堆栈,应用程序与操作系统使用不同的堆栈并且完成从用户态到内核态的转换,这个开销比函数调用大(跨越操作系统边界的开销),但安全
操作系统管理物理内存完成的功能
物理地址空间——硬件支持的地址空间
逻辑地址空间——一个运行的程序所拥有的地址空间
编译器将基于符号的地址变成逻辑地址,操作系统完成逻辑地址到物理地址的映射
操作系统简单的内存管理方法
无论用哪种都会产生内存碎片(内碎片和外碎片)
减少碎片的方法
连续内存分配的缺点:
非连续内存分配的优点:
缺点:
两种硬件方案:
分段
将连续的逻辑地址空间分段成多个物理地址空间
分段寻址硬件实现方案:
从逻辑地址开始,逻辑地址由段号和偏移组成,通过段号找到段所在物理内存的起始地址,具体的,段号指定了段表中的一个位置,段表中存储着逻辑地址段号和物理地址段号之间的对应关系,以及段的长度限制,随后偏移和段表中的起始地址加起来得到物理地址,其中,段表是由操作系统建立的
分页
分页是现在大部分CPU中采取的非连续内存管理方式
分段和分页的最大区别:在分段中,段的尺寸是可变的,分页中页的大小是固定的
帧(frame)
物理内存被分割为大小相等的帧,一个内存物理地址是一个二元组(f,o)
f——帧号(F位,共有2^F个帧)
o——帧内偏移(S位,每帧有2^S字节)
物理地址=2^S*f+o
页(page)
一个程序的逻辑地址空间被划分为大小相等的页
页寻址机制
页表保存了逻辑地址——物理地址之间的映射关系,页表以页号为索引,存放的是帧号,页表也是由操作系统建立的
页寻址机制
页表
页表就是一个大数组,索引是页号,存储的是帧号,页表中有一些Flag,可表明页帧是否存在,不存在则抛出内存访问异常
分页机制的性能问题
解决上述问题
Translation Look-aside Buffer(TLB)
TLB是CPU中MMU(内存管理单元)里的Cache,用来缓存近期访问的页帧装换表项,从而提升访问速度,对于TLB找不到的情况,称为TLB的缺失,在写程序时,尽量保证访问的局部性,从而避免TLB的缺失(miss)
时间换空间
二级页表(减少内存消耗)
多级列表
反向页表
index为帧号,值为页号,此时页表的大小只与物理地址空间大小相关,与逻辑地址空间大小无关,更节省空间
解决内存容量不够的问题:overlay、swap、虚拟内存
覆盖技术
目标:在比较小的可用内存中运行较大的程序
原理:把程序按照其自身逻辑结构,划分为若干个功能上相对独立的程序模块,那些不会同时执行的模块共享同一块内存区域,按时间先后来执行
缺点:增加编程难度、时间换空间
交换技术
目标:多道程序在内存中时,让正在运行的程序或需要运行的程序获得更多的内存资源
方法:可将暂时不能运行的程序送到外存,从而获得空闲内存空间
虚存技术
虚拟内存管理技术
覆盖技术:增加了程序员的负担
交换技术:增加了处理器的开销
目标:
程序的局部性原理(principle of locality):指程序在执行过程中的一个较短时期,所执行的指令地址和指令的操作数地址,分别局限于一定区域,这可以表现为:
虚存技术的基本概念:(可在页式或段式内存管理的基础上实现)
基本特征:
虚拟技术–虚拟页式内存管理
后备存储
虚拟内存性能:有效存储器访问时间(effective memory access time)EAT
EAT = 访问时间×页表命中几率+page fault处理时间×page fault几率
程序的局部性特点使得p小,从而效率更高
记录一个进程对页访问的一个轨迹,模拟一个页面置换行为并记录产生缺页数的数量
**Belady现象:在采用FIFO算法时,有时会出现分配的物理页面数增加,缺页率反而提高的异常现象
操作系统动态分配每个程序的页帧数
工作集:一个进程当前正在使用的逻辑页面集合,(某时间过去一段时间访问到的逻辑页面的集合)
常驻集:当前时刻,进程实际驻留在内存当中的页面集合
希望两个集合尽量重合,减少缺页中断
常驻集大小达到某个数目后,再给它分配更多的物理页面,缺页率页不会明显下降
此时我们希望操作系统将某个进程中多余的页帧分配给别的进程,从而实现整体的缺页中断次数尽可能少
随着程序执行,只要页不属于工作集窗口,就丢掉,从而确保物理内存有足够的空间,给其他程序提供更多内存,可以确保整个系统的缺页次数降低
通过缺页率来动态调整每个程序的常驻集大小(物理页数),保证每个程序的缺页率平衡。具体的,计算两次缺页中断的间隔,大于某个阈值时,去掉两次中断间没有在工作集中断页,小于某个阈值时,加上导致缺页中断的页
抖动问题:如果分配给一个进程的物理页面太少,不能包含整个的工作集,即常驻集包含于工作集,那么进程将会造成很多缺页中断,需要频繁换入换出,从而运行速度变慢,这种状态称为“抖动”
原因:随着主流内存的进程数增加,分配给每个进程的物理页面不断减小
MIBF(mean time between page faults)平均缺页时间
PFST(page fault service time)页缺失服务时间
CPU的使用率是去除了换入换出的负担计算的
希望找到中间的平衡点,使得两次缺页间隔的平均时间和完成一个缺页服务的时间尽量相等,此时系统在运行很多程序的情况下,也有很高的CPU利用率
一个具有独立功能的程序在一个数据集合上的一次动态执行的过程
进程≠程序,进程是一个动态执行的过程,程序只是静态的代码
进程的特点:
描述进程的数据结构,Process Control Block,PCB,操作系统为每个进程都维护了一个PCB,用来保存与该进程有关的各种状态信息
进程控制块:操作系统管理控制进程运行所用的信息集合
使用进程控制块
PCB的组成方式:链表
进程的三种基本状态:进程在生命结束前处于且仅处于三种基本状态之一
进程没有占内存空间,称为进程挂起,与进程阻塞不同,挂起(suspend)把一个进程从内存转到外存
状态队列
为什么使用线程?
并发且共享地址空间(进程并不能共享地址空间)
线程就是进程当中的一条执行流程
线程=进程共享资源
线程的优点:
线程的缺点:
一个线程崩溃,会导致其所属进程的所有线程崩溃
主要有三种线程实现方式:
用户线程机制,不依赖于操作系统的内核,由一组用户级的线程库函数来完成线程的管理
用户线程缺点:
内核线程是指在系统的内核当中实现的一种线程机制,由操作系统的内核来完成线程的创建、终止和管理
轻量级进程(Solaris/linux)
内核支持的用户线程,一个进程可有一个或多个轻量级进程,每个量级进程由一个单独的内核线程来支持
停止运行当前进程并且调度其他进程
上下文:寄存器、CPU状态…
fork()的简单实现,完全复制父进程的地址空间,复制操作代价昂贵
wait()系统调用是被父进程用来等待子进程的结束,帮助释放子进程的PCB等资源
进程结束执行之后,它调用exit()
子进程执行完exit,父进程还未执行wait时,此时子进程处于zombie状态,如果此时父进程先死亡了,子进程就会一直处于zombie状态,一般每隔一段时间root进程会扫描是否存在僵尸状态的进程,如果有,它会代理完成资源回收操作
上下文切换
CPU调度
执行模型:程序在CPU突发和I/O中交替
每个调度决定都是关于在下一个CPU突发时将哪个工作交给CPU
评价指标
CPU使用率:CPU处于忙状态所占时间的百分比
吞吐量:在单位时间内完成进程的数量
周转时间:
等待时间:进程在就绪队列中的总时间
响应时间:从一个请求被提交到产生第一次响应花费的总时间
减少响应时间
减少平均响应时间波动
增加吞吐量
减少等待时间
上述指标是矛盾的
低延迟调度增加了交互式表现
吞吐量是操作系统的计算带宽
响应时间是操作系统的计算延迟
实时调度:正确性依赖于其时间和功能两方面的一种操作系统
多处理器调度
优先级反转:低优先级任务影响高优先级任务的现象(例如:高优先级任务依赖的低优先级任务被中间优先级的任务抢占的现象)
优先级继承:低优先级任务继承高优先级任务的优先级
独立的线程:不和其他线程贡献资源或状态,具有确定性和可重现性,调度顺序不需要
合作线程:在多个线程中共享状态,不确定性,不可重现
调度带来的问题
上面这种现象称为Race Condition(竞态条件)
系统缺陷:结果依赖于并发执行或者时间的顺序/时间:不确定性、不可重现
Atomic Operation(原子操作)
原子操作是指一次不存在任何中断或者失败的执行,可以避免上述问题
Critical section(临界区)
临界区是指进程中的一段需要访问共享资源并且当另一个进程处于相应代码区域时便不会被执行的代码区域
Mutual exclusion(互斥)
当一个进程处于临界区并访问共享资源时,没有其他进程会处于临界区并且访问任何相同的共享资源
Dead lock(死锁)
两个或以上的进程,在相互等待完成特定任务,而最终没法将自身任务进行下去
Starvation(饥饿)
一个可执行的进程,被调度器持续忽略,以至于虽然处于可执行状态却不被执行
互斥:同一时间临界区中最多存在一个线程
Progress:如果一个线程想要进入临界区,那么它最终会成功
有限等待:如果一个线程i处于入口区,那么在i的请求被接受之前,其他线程进入临界区的时间是有限制的
无忙等待:如果一个进程在等待进入临界区,那么在它可以进入之前会被挂起
禁用硬件中断:
没有中断,没有上下文切换,因此没有并发,进入临界区,禁用中断,离开临界区,开启中断
但是一旦中断被禁用,线程就无法被停止,无法限制响应中断所需的时间,要谨慎使用
基于软件的解决方案:
更高级的抽象(广泛使用):
基于硬件原子操作的高层抽象实现
硬件提供了一些原语:中断禁用、原子操作等
操作系统提供更高级的变成抽象来简化并行编程,如锁,信号量
锁是一个抽象的数据结构
通过test-and-set或exchange来实现
锁是更高等级的变成抽象
常用的三种实现方法
可选的实现内容:
信号量可以用在2个房秒
例子:生产者和消费者,生产者写数据时,消费者不可以读取数据,但生产者或消费者可以是多个一起访问数据
信号量容易出错
目的:分离互斥和条件同步的关注
什么是管程:
一个锁:指定临界区
0个或者多个条件变量:等待/通知信号量用于管理并发访问共享数据
Lock
Condition Variable
详看视频中的例子
死锁问题,进程间相互等待的问题
资源分配图:
资源指向进程:被进程使用
进程指向资源:需要资源
如果途中不包含循环,无死锁
如果图中包括循环,每个资源只有一个实例->死锁,多个实例->可能死锁
死锁的四个必要条件:
死锁预防:
死锁避免:
死锁避免算法动态检查分配状态,以确保永远不会又一个环形等待状态
系统处于安全状态指:针对所有进程,存在安全序列
避免进入包含死锁的不安全状态
银行家算法:
银行家算法是一个死锁避免的著名算法,银行家在客户申请的贷款数量不超过自己拥有的最大值时,都应尽量满足客户的需要
前提条件:
基于上述前提条件,银行家算法通过尝试寻找允许每个进程获得的最大资源并结束(把资源返还给系统)的进程请求的一个理想执行时序,来决定一个状态是否是安全的
死锁检测和死锁恢复
判断资源等待图是否存在环(资源分配图中去掉资源节点)开销比较大(很少实际使用),选择某个进程杀死
IPC(Inter-Process communication)进程间通信
直接通信:通信链路
简介通信:定向从消息队列接收消息
阻塞(blocking):同步(发送完成前不继续进行)
非阻塞(Non-blocking):异步
几个进程间通信机制:
文件系统:一种用于持久性存储的系统抽象
文件:文件系统中一个单元的相关数据在操作系统中的抽象
文件头:在存储元数据中保存了每个文件的属性
文件描述符:操作系统为每个进程维护一个打开文件表,一个打开文件描述符是这个表中的索引
需要元数据来管理打开文件:
用户试图:持久的数据结构
系统访问接口:字节的集合
文件系统中所有操作都是在整个磁盘块空间上进行的
文件以目录的方式组织起来
文件系统的种类
虚拟文件系统,屏蔽文件系统差异性,为上层提供统一的接口
数据缓存:基于分页的数据缓存方式,类似页的换入换出机制
文件分配(文件所占空间的分配):
空闲空间列表:
跟踪在存储中所有未分配的数据块
多磁盘管理RAID
通常磁盘通过分区来最大限度减小寻道时间
分区:硬件磁盘的一种适合操作系统指定格式的划分
卷:一个拥有一个文件系统实例的可访问的存储空间
寻道时间是性能上区别的原因