1、操作系统概念
基本的概念之类的,面试很少问。以防万一在这里列个大概
1.1、操作系统功能
操作系统的功能可以分为以下三点
- 作为计算机系统资源的管理者:处理器管理、存储器管理、文件管理、设备管理
- 为用户和程序提供方便的接口:命令接口、程序接口(系统调用)
- 与没有软件的裸机相比,硬件软件作为扩充机器构成虚拟机
1.2、操作系统特征
- 并发:线程可以并发执行
- 共享:计算机资源允许并发执行的进程共享,分为互斥共享和同时共享
- 虚拟:逻辑与物理的对应,如空分复用的虚拟存储器技术和时分复用的虚拟处理器技术
- 异步:进程的执行以不可知的速度进行
1.3、操作系统体系架构与运行机制
1.3.1、体系架构与运行机制
运行机制
- 两种指令:特权指令(IO指令、中断指令、缓存清零指令等),非特权指令(普通运算指令)
- 两种处理器状态:核心态(管态,所有指令可执行)与用户态(目态,只能执行非特权指令),中断是切换管态和目态的唯一途径
- 两种程序:运行在核心态的内核程序、运行在用户态的应用程序
操作系统内核
内核是操作系统最基本与核心的部分,实现三个基本功能:时钟管理、中断管理和原语
- 时钟管理:提供标准时间、提供时钟中断(比如分时复用方案中的时间片轮转制度就要用到时钟中断)
- 中断管理:操作系统各种操作的基础
- 原语:一种特殊的程序,存在以下特点
- 最接近硬件
- 原语的操作具有原子性
- 运行时间短,调用频繁
资源管理功能
进程管理、存储器管理、设备管理
操作系统体系架构
- 大内核:所有核心功能作为整体运行于核心态,效率高但运维困难
- 小内核:只在核心态保留最基本的功能,运维方便,但频繁进行管态目态切换,效率低
1.3.2、中断与异常
中断的概念
中断是为了多个进程的并行操作所产生的,发生中断意味着需要操作系统介入展开管理,发生中断后操作系统会进入核心态
中断分类
- 内中断:内中断来源于cpu的内部,可以分为自愿中断(陷入指令)和强迫中断(缺页,整数除0),也可以分为陷入(如系统调用)、故障(可修复的,如缺页中断)、终止(不可恢复,如整数除0)
- 外中断:这也是侠义的中断,来源于cpu外部,如IO设备的中断信号、用户设置的中断
外中断执行过程
- 关中断,cpu响应后拒绝响应更高级的中断程序的中断请求
- 保存断点,即保存原来cpu正在运行的程序的断点(程序计数器PC)
- 中断服务程序寻址
- 保存现场和屏蔽字
- 开中断,方便cpu并行地响应中断请求
- 执行中断服务程序
- 关中断
- 恢复现场和屏蔽字
- 开中断,中断返回
1.3.3、系统调用
系统调用概念
系统调用是OS提供给应用程序的一系列接口,APP可以通过系统调用获得OS的服务。系统调用会使操作系统从用户态切换到核心态,包括进程控制、进程通信、内存管理、文件管理、设备管理等系统调用。
系统调用的执行过程
- 用户程序执行陷入指令(trap,访管指令)产生内中断,OS进入核心态
- OS对系统调用进行处理
- 处理完成后交还cpu使用权给用户
2、进程管理
进程管理相关的基本模型,以及Linux中进程同步、通信所采用的解决方案
2.1、进程与线程
2.1.1、进程的概念
进程的定义
- 通常将进程实体简称为进程,由程序段、数据段、PCB(Process Control Block)所组成,进程实体是进程存在的标志。进程可以看作是程序的一次执行过程,是一个动态存在的概念。
- 进程是系统进行资源分配和调度的基本单位。
进程的特征
动态、并发、独立、异步、结构
进程实体组成
进程实体由三个部分组成:程序段、数据段、PCB
- 程序段:存放程序代码
- 数据段:全局变量、局部变量、常量等等
- PCB:操作系统所需要的,用于描述进程当前情况的所有数据,包括PID、进程优先级、进程状态、组员分配清单以及各种寄存器的值等
进程组织
也就是如何把一系列进程组织起来以方便OS管理
- 链接方式:创建执行指针、就绪队列、阻塞队列等几个队列,操作系统持有各个队列的指针,通常优先级高的在队首
- 索引方式:根据执行状态建立索引表,操作系统持有各个索引表
2.1.2进程运行状态及其转换
进程的状态
三种基本状态:执行、就绪、阻塞,将进程执行所需的资源分为CPU和其他资源,则三种状态下
进程状态 | CPU资源 | 其他资源 |
---|---|---|
执行 | 已获得 | 已获得 |
就绪 | 未获得 | 已获得 |
阻塞 | 未获得 | 未获得 |
除了这三种基本状态以外,还有创建和终止两种状态,对应了PCB的创建资源的分配以及PCB的撤销和资源的回收
进程之间的状态转换
进程控制就是实现对进程的状态的切换
进程进入就绪队列依次执行,时间片用完或需要某事件唤醒则加入某事件的阻塞队列当中
2.1.3 进程通信
为了进程的安全,进程不允许访问其他进程的地址空间,因而为了方便进程之间的通讯,引入了三种进程通信模型
共享存储
互斥地对某一个存储区域进行读写操作,有基于数据结构的贡献存储和基于存储区的共享存储
管道通信
- 设计单向流动的管道,进程对管道互斥地进行访问
- 管道写满时,写进程将被阻塞;读进程同理
- 管道没写满时不允许读;管道没读空也不允许写
- 数据一旦被读出,就会被抛弃,因此读进程只能有一个
消息传递
设置格式化的消息,使用发送和接收原语进行操作,分为直接通信和间接通信
- 直接通信:消息挂在接收进程的消息缓冲队列上
- 间接通信:类似邮箱的机制
2.1.4、线程与协程
线程的概念
- 一个进程通常要”同时“做很多事,因此为了进程的并发性,引入了线程。
- 线程是CPU执行流的基本单元,也就是cpu调度的基本单位,引入线程后,进程成为了除cpu之外的资源分配的基本单元
- 进程的切换需要进行进程环境的切换,线程的切换代价比cpu小得多
线程的属性
- 线程是cpu调度的基本单位
- 多cpu计算机中各个线程可以占用不同的cpu
- 每个线程都有线程id,线程控制块TCB
- 线程也有运行、就绪、阻塞三种基本状态
- 线程几乎不拥有系统资源
- 同一进程的不同线程共享进程的资源
- 同一进程中线程间的通信几乎无需系统干预,且系统开销极小
- 同一进程下线程切换不引起进程切换,不同进程下线程的切换会引起进程切换
线程的分类
- 用户级线程:应用程序用线程库所实现的线程,在用户态下即可完成
- 内核级线程:对cpu而言的线程,由cpu进行管理,必须在核心态下完成。内核级线程才是cpu资源分配的基本单位
根据用户级线程和内核级线程的对应关系可以将多线程模型分为”多对一“、”一对一“、”多对多“模型
关于协程
协程的特点
- 协程也是为了并发操作而设置的,是比线程更加轻量级的存在。一个线程可以拥有多个协程
- 协程是一种特殊的函数,这个函数可以在某个地方挂起,也可以继续运行,因此协程和线程进程不是一个维度的概念
- 协程虽然是为了解决并发问题(比如针对IO的处理),但是仍然是串行执行的,不能利用cpu的多核能力
- 协程完全是处在用户态执行的,协程的切换不会像线程切换那样耗费系统资源
2.2、处理机调度
2.2.1、处理机调度层次
- 高级调度(作业调度):从外存到内存的调度,建立与撤销PCB
- 中级调度(内存调度):将暂时不能运行的进程调到外存中并设置为挂起状态,但PCB不会调出,挂起进程的PCB会被放到挂起队列中
- 低级调度(进程调度):将就绪队列的进程调给cpu
2.2.1、进程调度时机与方式
进程调度时机
- 进程主动放弃处理机:进程结束、进程发生异常、进程主动请求阻塞(如IO)
- 进程被动放弃处理机:时间片用完、有更高优先级的进程进入就绪队列、更紧急的事情要处理(如IO中断)
不能进行调度的时刻
- 处理机正在处理中断
- 原子操作过程中
- 进程在操作系统的内核程序临界区中,内核临界区是用来访问某种内核数据结构,如就绪队列的,为了减少进程切换的开销,进程在操作系统内核临界区中是不允许进行进程的调度与切换的
进程调度方式
- 非抢占方式:处理机的进程主动放弃处理机
- 抢占方式:进程可以根据优先级等方式进行处理机的抢占
2.2.1、进程调度算法
进程调度算法评估指标
- CPU利用率:CPU忙碌时间 / 总时间
- 系统吞吐量:完成的工作数 / 时间
- 平均周转时间:各作业的周转时间(从提交给系统到结束的时间) / 作业数
- 带权周转时间: 作业周转时间 / 作业实际运行的时间
- 平均带权周转时间: 周转时间之和 / 作业数
- 等待时间 : 从进程建立到开始处理的时间
- 响应时间 : 用户提交请求到首次产生响应的时间
FCFS 先来先服务
优先处理先来的进程,非抢占式的算法,不会导致饥饿
SJF 短作业优先
优先处理短作业,非抢占式算法,容易导致长作业饥饿,也有抢占式的版本,即最短剩余时间优先算法
HRRN 高响应比优先算法
等待时间与要求服务时间 / 要求服务时间值最大的服务优先,是上述算法的折中版本
时间片轮转算法
为每个进程分配时间片,通过时钟中断进行抢占。时间片太短会增大进程切换的开销,太大会增大请求的响应时间
优先级调度算法
根据进程优先级选取优先级高的进程进行服务
多级反馈队列调度算法
设计多个优先级从高到低,时间片从小到大的队列,进程进入高级队列交由cpu处理,时间片用完后进程依次进入低级队列。
- 只有当高优先级队列为空时才会处理低优先级队列的进程,因此该算法是会导致饥饿的。
- 高优先级队列中的进程会抢占低优先队列进程的处理机,被抢占的进程会放回原队列队尾
- 这也是Unix的解决方案
2.3、进程同步与互斥
2.3.1、基本概念
- 进程同步又称直接制约关系,为了解决进程异步,也就是进程以不可预知的速度推进的问题,引入同步保证进程执行的先后顺序或者避免多个进程抢占同一临界资源
- 进程呼出又称间接制约关系,目的在于临界资源被进程访问的时候其他进程若要访问临界资源必须等待
对临界资源的互斥访问,可以分为四个步骤
- 进入区,检查能否进入临界区,可以的话就设置相关的临界资源访问标志,也就是上锁
- 临界区,访问临界资源
- 退出区,解锁
- 剩余区,其他后续处理
互斥访问所应该遵循的原则
- 空闲让进,临界资源空闲时应允许进程访问
- 忙则等待,临界资源被访问时不允许其他进程访问
- 有限等待,访问临界资源的进程不会饥饿
- 让权等待,进程无法访问临界资源时应让出虚拟机
2.3.2、同步与互斥实现方式
进程同步的实现方式
信号量(整型+记录型)、互斥量、管程、进程间通信
进程互斥的实现方式
- 硬件实现:开关中断、TS指令、XCHG指令
- 软件实现:单标志检查、双标志先/后检查、皮特森算法
信号量机制
一组记录系统资源量的信号量。对其有PV操作的原语,对应等待资源和释放资源,有两种信号量
- 整型信号量,存在忙等问题
- 记录型信号量,可以通过阻塞和唤醒机制解决忙等问题
管程
为了解决信号量操作复杂编写不便而引入,有以下几个部分组成
- 管程名称
- 局部于管程的共享数据量
- 管程的操作过程
- 管程初始化语句
存在以下特点
- 必须通过特定的入口访问管程(封装)
- 每次只允许一个进程在管程内执行某个内部过程
2.3.4、死锁
2.4.1、死锁概念
各个进程都在等待其他进程手里的资源导致各个进程无法进一步推进
- 死锁至少两个进程才能发生,饥饿一个进程也能发生,二者都是操作系统的问题,而死循环是进程本身的问题
2.4.2、死锁发生的条件
- 互斥条件
- 不可剥夺条件
- 请求和保持条件:请求阻塞时会对自己已经持有的资源不释放
- 循环等待条件:存在循环的等待链
2.4.2、死锁处理
- 预防死锁:破坏四个死锁的发生条件之一
- 破坏互斥:为安全性带来问题
- 破坏不可剥夺:反复申请和释放资源,系统开销大
- 破坏请求和保持:一次性申请完资源再执行会导致效率低下
- 破坏循环等待:难以扩充
- 避免死锁:银行家算法,找出安全序列
- 死锁检测:用资源分配图的数据结构进行简化,若存在不可消除边,则有死锁。
- 死锁接触:剥夺资源、撤销进程、进程回退
2.5、Linux的处理机调度
2.5.1、Linux进程调度
Linux设置了默认调度类和实时调度类来分别处理正常的和实时进程的调度。Linux设置了Policy
关键字来确定进程的调度策略,总体可以分为两类,分别是实时调度进程和批处理的调度进程,实时调度进程中实现了先来先处理FIFO
和轮询RR
的调度方法,其他进程采用的是完全公平调度算法CFS
。实时进程的优先级是高于正常进程的优先级的,操作系统也可以动态地调整Policy
,比如一个计算进程在设置输入参数的时候使用实时进程的处理方法,输入完成后更改Policy
,转向使用CFS算法。
完全公平调度(CFS)给每一个进程设置了虚拟时钟vurtime
,记录进程的运行时长,根据优先级的不同设置有不同的衰减因子,优先级高的进程虚拟时钟增长的就慢。每次处理机选择虚拟时钟最短的进行处理。
所有的可以运行的任务都被挂在在红黑树上,同时缓存了红黑树的最小节点,每次选择vurtime
最小的任务进行处理。注意
- 红黑树的key是
vurtime
-min_vurtime
,这么做主要是为了防止key溢出。 - 阻塞任务会被从红黑树上删除
- 找到红黑树最左侧节点的时间复杂度是
O(logn)
,但是由于缓存了最左侧节点,可以视为是O(1)
的调度算法
2.5.2、Linux进程同步机制
- 互斥锁
- 读写锁:读写、写写间互斥、允许读读
- 自旋锁
- 信号量:记录资源数量与等待资源的线程数
- 条件变量:对于设置有缓存区的生产者/消费者模型,如果生产快于消费会导致缓存区满而撑死,消费快于生产会导致缓存区满而饿死。条件变量可以让调用线程在特定情况下暂停。
- 栅栏/屏障:协调多个线程并行工作的机制,栅栏允许每个线程等待,知道所有合作线程都达到某一点,然后从该点继续执行。
2.5.3、Linux进程通信机制
管道
- 匿名管道与命名管道
- 匿名管道是半双工的,只能向一个方向流动,且读与取互斥。命名管道是文件,因此可以实现双向通信
- 匿名管道只用于有父子或有相同祖先,子进程写,父进程读,而命名管道则可以通过管道的路径名实现管道的互相通信(Linux的新进程都是从父进程复制而来,随后进行相关调整的,因此具有父子关系)
- 匿名管道来自操作系统的内核缓存,命名管道本质上是一个文件
信号
有硬件来源和软件来源,如按下键盘,kill指令等
消息队列
存放在内存中的消息队列标识符
共享内存
使得多个进程都能够读写某一块内存空间,是针对其他通信方式效率低而设计的
信号量
用于标识资源是否可用
套接字
套接字是更为基础的进程间通信,不同的是它还可以做不同机器之间的进程通信,需要通过网络协议等方式来实现
3、内存管理
3.1、基本内存管理
3.1.1、内存分配与回收
连续分配管理
指的是为用户进程分配一段连续的内存空间
- 单一连续分配:用户区只有一道用户程序,无外部碎片
- 固定分区分配:划分固定的分区装入不同的用户程序,无外部碎片
- 动态分区分配:动态划分分区装入不同的用户程序,无内部碎片
空闲分区可以使用分区表或者分区链进行管理
动态分区算法
- 首次适应法:只要碰到能装入的空闲内存区域就装进去,算法开销小
- 最佳适应法:选择能放进去的最小位置,空闲分区链递增链接
- 最坏适应法:选择能放进去的最大位置,空闲分区链递减链接
- 临近适应法:每次从上次搜索的位置开始搜索,算法开销小
非连续分配管理
- 分页存储管理:
- 把内存分成一个个相等的小分区,根据分区大小将进程拆分成一个个小的部分装入,通过页码+偏移量的方式获取物理地址,要知道用户进程每一页在地址中的存放位置,就引入了页面号与块号对应的页表的概念。
- 为了查询快速,引入了块表,查询过程会首先根据页号查询块表,块表没命中则查询页表,之后更新快表中的内容,会设置有快表 的淘汰策略,主要是根据频率等。
- 当页面过多时就会使得虚拟地址过长,因此引入多级页表,分组的进行查找,是空间换时间的方式。因为不是所有的页表项都会存在内存,根据局部性原理只要保留常用的,其他的在需要的时候再调入内存即可。
- 分段存储管理:根据程序的自身逻辑进行分段,每段占据连续空间,段之间不是离散的
- 段页式存储管理:二者的结合,分段,段内部可以分页
3.2、虚拟内存管理
3.2.1、虚拟内存引入
局部性原理
- 时间局部性:执行过的代码在不久后很可能再次执行
- 空间局部性:访问过的内存空间在不久后可能再次访问
传统内存的缺陷
- 一次性:作业需要一次性地放入内存中,这就导致大的作业难以运行
- 驻留性:一旦引入就长时间贮存,占用了内存空间
虚拟内存的定义
在程序的执行过程中,操作系统将程序很快会用到的部分装入内存,暂时不用的留驻外存。当信息不存在时操作系统将信息从外存调入内存,当内存空间不足时,将暂时用不到的信息换出内存,这样就使得系统的内存在硬件没有扩充的情况下扩大了,这也体现了操作系统的虚拟性,操作系统的虚拟内存的最大值取决于计算机寻址的最大范围,实际虚拟内存取决于计算机寻址最大范围和内存实际容量的最小值。
虚拟内存存在以下特点
- 多次性:作业多次装入
- 对换性:信息在内外村之间对换
- 虚拟性:逻辑上扩充了内存
3.2.2、请求分页管理方式
虚拟内存的实现依托于非连续分配存储,对这些离散的信息进行内外存之间的对换
页表机制
页表中的页表项存放了内存块号、是否进入内存的状态位、记录访问时间与次数的字段、是否修改过的标志位、外存地址
缺页中断机构
当查询页表发现要访问的页面没有在内存当中时就会产生一个缺页中断,由中断处理程序处理中断,将产生缺页中断的进程阻塞,当调页完成后将进程唤醒,放回就绪队列。
如果内存有空闲块,就为进程分配,没有的话就通过页面置换算法选择一个页面进行淘汰。如果页面被修改过就写回内存。
地址变换机构
也设置有快表,同时要处理缺页中断
3.2.3、页面置换算法
- 最佳置换算法:淘汰最长时间不会被使用的算法,缺页率最低,但是无法实现
- 先进先出置换算法:实现简单,但性能差
- 最近最久未使用置换算法:性能很好,但需要硬件的支持
- 时钟置换算法:页表连成循环的链表,每次优先置换未访问过的也就是访问位是0的,同时第一轮把标志位是1的都置0,最多2次循环
- 改进型的时钟置换算法:在时钟算法的基础上考虑修改,优先置换未访问未修改的,然后是访问过未修改的,最后是访问过且修改的,最多4此循环
3.2.4、页面分配策略
驻留集
请求分页存储管理中给进程分配的物理块的集合,一般取决于进程的大小
页面分配于置换策略
- 固定分配:为每个进程分配固定大小的物理块
- 可变分配:为每个进程分配可变大小的物理块
- 局部置换:发生缺页时仅置换自己进程的物理块
- 全局置换:可以置换其他进程的物理块
页面调入调出位置
- 系统有足够的交换区:页面的调入调出都是在交换区进行,速度更快
- 系统缺少足够的交换区:不会修改的页面从文件区调换,修改的部分换出时先写入交换区。
抖动现象
页面频繁地调入调出的过程,主要是由于分配给进程的物理块不够,或者说工作集比驻留集还大造成的
参考
https://blog.csdn.net/weixin_43914604/article/details/104415990