操作系统简记| 青训营笔记

132 阅读12分钟

操作系统简记

中断、异常和系统调用

区别

  • 处理时间

    • 中断:异步
    • 异常:同步
    • 系统调用:异步或同步

异步:不知道什么时候会产生

  • 响应

    • 中断:持续,对用户应用程序是透明的
    • 异常:杀死或者重新执行意想不到的应用程序指令
    • 系统调用:等待和持续

系统调用

如C语言调用printf时调用的是内核态的write()

函数调用

只存在一个栈空间,系统调用存在两个栈(因此系统调用时候需要有堆栈切换)

系统调用开销大于函数调用

物理内存管理

不同进程看到的是一段连续可以使用的地址空间,实际上在磁盘中这些空间不一定连续(逻辑地址空间:运行程序看到的空间)

进程多的时候,会出现内存不够,操作系统会将最需要放在内存的数据放在内存,其他一些数据临时地放在硬盘上(虚拟内存)。

物理地址空间:硬件支持的地址空间

逻辑地址空间:一个运行的程序所拥有的内存范围(一个程序中,一个变量的名字就是一种逻辑地址)

ALU:计算逻辑单元

MMU:内存管理单元

连续内存分配和内存碎片

内部碎片,比如程序需要510字节,但是最小只能分配512字节,2字节就是内部碎片。

动态分区分配

最先:找的第一个大于程序指定的大小,就ok

最佳:全部扫一遍,看哪个最合适,大的最小的一个

最差:大的最多的

释放慢:因为不是按地址排序的,但是合并的时候需要查询相邻地址是否空闲

主要是:空闲列表排序方式,查询和释放地址的开销。

碎片整理

通过调整进程占用的分区位置,来减少或避免分区碎片

分区对换

通过抢占并回收处于等待状态进程的分区,以增大可用内存空间。

非连续分配内存

外排序:每次读一部分进来排序,然后存进外存;最后重新捋一遍。

段>页

段:访问方式和存储数据等属性想相同的一段地址空间

每个段对应一个连续的内存“块”

基于段的访问内存方式:

页式存储管理

基于页真的物理地址计算:用二元组表示

逻辑地址空间和物理地址空间的划分是类似的

中间的映射由页表完成

页内偏移 = 帧内偏移

页式存储管理机制的性能问题:

访问一个内存单元需要2次内存访问:1、获取页表项; 2、访问数据

快表:使用缓存

命中,直接得到物理地址;不命中,查内存,并存到快表

多级页表

将页号分层

访问内存k + 1次

往往进程用不到所有页表内容,所以这样方式可以减少实际页表大小

实例:

大地址系统下(比如说64位系统),页表还是会比较多,“页寄存器”可以解决这一问题。一页对应一个页寄存器。

页寄存器缺点:由于需要完成逻辑地址到物理地址的转换,所以需要搜索页寄存器存的是哪个逻辑地址页表,所搜会比较困难。

一个解决方式:哈希

虚拟存储

交换、覆盖

交换

交换技术面临的问题

局部性原理

这些数据全在内存中的话,使用时间是一样的。如果物理内存的量小,使用了虚拟存储,局部性特征不怎么好,那么时间差距是很大的。

虚拟存储做的事

把原来放到内存的进程地址空间信息,把一部分放到外存中。

由操作系统来实现,且一部分一部分(不以进程伟单位)导到外存里去。

虚拟存储的基本概念

虚拟内存的基本特征

1、不连续

2、大用户空间

虚拟页式存储管理

在页式管理的基础上,增加请求调页和页面置换

虚拟页式存储中的地址转换

页表中有一些控制位

缺页异常

概念:发现页表项中这一页不在物理内存中,产生缺页异常,缺页异常将内容读到内存中,再重新执行这条指令。

缺页异常处理大致流程:

如果没找空间夜侦,就需要利用置换算法,将当前某一页写出去,然后占用。

页面置换算法

局部置换算法:不需要考虑分配给进程的物理页面数是多少。

全局:考虑不同进程之间对进程需要量的差异。

当出现缺页异常,需要调入新页面而内存已满的情况时,置换算法选择被置换的页面。

  • 最优页面置换算法
  • 先进先出算法
  • 最近最久未使用算法
  • 时钟页面算法
  • 最不常用算法

最近最久未使用算法

示例

每次都需要更新内存中页表的使用时间

Belady现象

FIFO算法会有此现象

可能出现:分配的物理页面增加时候,缺页次数反而升高的异常现场。

原因:FIFO算法的置换特征与进程访问内存的动态特征矛盾,简单点说就是换出去的正好是进程需要访问的内存。

clock算法是FIFO和LRU的折中

全局置换算法

全局置换算法为进程分配可变数目的物理页面

工作集置换算法

缺页率算法

通过调节常驻集大小,使每个进程的缺页率保持在合理范围

缺页率算法和工作集算法区别:工作集算法每次都需要更新维护,缺页率算法不需要,在时间间隔间隔足够大的时候才会更新集合。

负载控制

进程多,每个进程的物理页面少,进一步地,缺页会增多。这就需要通过调节并发进程数来进行负载控制。=> 控制内存大小

进程和线程

进程:具有一定独立功能的程序再一个数据集合上的一次动态执行过程

进程是操作系统处于执行状态程序的抽象

程序 = 文件(静态的可执行文件)

进程 = 执行中的程序 = 程序 + 执行状态

进程的特点:动态性,并发性,独立性,制约性

进行是动态的,程序是静态的

程序是有序代码的集合,进程是程序的执行,进程有核心态/用户态

进程的暂时的,程序是永久的

进程控制块PCB

操作系统管理控制进程运行所用的信息集合。操作系统用PCB来描述进程的基本情况和运行变化的过程。每个进程都有唯一一个PCB。内容:标识信息,处理机现场保护,进程控制信息;例如PID,UID,调度优先级,寄存器

进程状态

三状态进程模型:就绪、运行、等待

挂起进程模型

处于挂起状态的进程映像再磁盘上,目的是减少进程占用内存

线程

希望在一个进程内也可以进行并发操作

线程缺点:一个线程崩溃,会导致所属进程的所有线程崩溃

线程分类:用户线程、内核线程、轻权进程——一个进程可有一个或多个轻量级进程,每个轻权进程由一个内核线程支持。

实践结果来看,一对一是较好的。

进程控制(感觉不是很重要)

进程切换

中断或系统调用时:保存进程状态到PCB(进程控制块)

进程控制块的信息:状态寄存器,地址空间起头位置,第一级页表位置;占用的资源信息:存储、堆栈、和要保护现场的信息。

进程创建

unix下的进程创建:

加载:exec()用新程序重写当前进程

系统没有用户进程的情况下,CPU会进行创建空闲进程。

进程加载

指用户的应用程序通过系统调用exec()加载,来完成新的可执行文件的加载。

进程的等待与退出

等待和退出是父子进程之间的交互,完成子进程资源的回收。

处理机调度

调度策略

比较调度算法的好坏:

前两个:系统利用率角度,后三个:用户角度。

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

先来先服务:周转时间长

短进程优先

允许抢占线程

特点:具有最优平均周转时间

缺点:

  • 可能导致饥饿,连续的短进程会使长进程无法获得CPU资源

  • 需要预知未来,需要预估下一个CPU计算的持续时间

    • 询问用户
    • 根据过去预测未来

最高响应比优先算法

避免了饥饿;(关注进程的等待时间,避免无限期推迟)

时间片轮转、多级反馈队列、公平共享调度算法

时间片:分配处理机资源的基本时间单元

按照先来先服务的原则,每个进程执行一个时间片。

多级反馈队列算法(KLFQ)

实时调度

可调度性:

多处理机调度

同步是多处理机调度的一个大问题

优先级反置

(大于2个进程)

两种解决方案:

同步互斥

原子操作:一次不存在任何中断或失败的操作

操作系统利用同步机制在并发执行的同时,保证一些操作是原子操作。

互斥:一个进程占用资源,其他进程就不可以使用资源

死锁:多个进程占用部分资源,形成循环等待;

饥饿:其他进程可能轮流占用资源,一个进程一直得不到资源

临界区

进程中访问临界资源的一段需要互斥执行的代码

临界区的访问规则

临界区的实现方法

禁用中断:其他进程无法对当前进程做打扰,但是对系统中断响应有影响

缺点:禁用中断后,进程无法被停止,整个系统会停下来,导致其他进程处于饥饿状态;临界区可能很长,无法确定响应中断所需的时间。

软件方法:共享变量协调方式来完成

在进去区需要不断查询共享变量的状态

高级抽象同步方法

锁在等待时是消耗CPU的。

无忙等待锁

Acquire中,如果锁被占用,将线程压进等待队列中,并且执行调度程序,让出CPU使用权。

Release: 将线程从等待队列弹出,压进就绪队列中。

原子操作指令锁的优点:

信号量与管程

信号量是操作系统提供的一种协调共享资源访问的方法

  • 软件同步时平等线程间的一种同步协商机制
  • OS时管理者,地位高于进程,信号量由OS负责管理
  • 用信号量表示系统资源的数量

(由荷兰科学家Dijkstra在20世纪60年代提出)

信号量的实现

信号量的使用

必须成对使用P和V操作

管程

改进信号量处理临界区的一些麻烦

哲学家就餐问题

死锁:比如哲学家就餐问题,五个盘子围一圈放,每两个盘子之间放一把叉子,在算法写的简单的情况下,对于每个叉子都建立信号量,在哲学家就餐时,需要对左右两个叉子进行P操作。在 五个哲学家都拿起了左边的叉子的情况下,也就是说都进行了第一个P操作,这时候没有叉子了,所有哲学家都要等下去,不会退出,就造成了死锁的情况。

改进的方案:

设一个全局的锁,把就餐的事变成临界区的操作

只能有一个人就餐

方案3

读者-写者问题

上述实现:读者优先

读者持续不断进入,则写者就处于饥饿;

饥饿状态例子:在读者-写者问题中,若采用读者优先的策略,即便是读者在写者后面进来,读者也会先读。

管程可以比较简单地实现读者优先/写者优先

写者优先:

死锁

死锁问题

由于竞争资源,两个或者更多线程在执行中出现,永远相互等待的现象

死锁处理方法

死锁预防

确保系统不会进入死锁状态,(四个条件不满足)

互斥:

互斥的共享资源封装成可同时访问

持有并等待:

进程请求资源时,要求它不持有任何其他资源;

进程在开始时候,申请所有需要的资源

非抢占:

进程请求不能立即分配的资源,则释放已占有资源

循环等待:

对资源排序,要求进程按顺序请求资源

死锁避免

在使用前进行判断,只允许不会出现死锁的进程请求资源。

利用先验信息,在分配资源时判断是否会出现死锁,只在不会死锁时分配资源。做法:

要求进程声明需要资源的最大数目

限定提供与分配的资源数量,确保满足进程的最大需求

动态检查资源分配状态,确保不会出现环形等待

死锁检测和恢复

检测到系统进入死锁后,进行恢复

一般由应用进程来处理

银行家算法

基本思路:当前剩余资源可以满足某一个线程的未来需要,迭代到最后可以满足所有线程的未来需要,那么就认为找到了一个安全系列,使得系统是安全的。

死锁检测

允许系统进入死锁状态,维护系统的资源分配图,定期调用死锁检测算法来搜索图中是否存在死锁;出现死锁时,用死锁恢复机制进行恢复。