2022大厂高频面试题之操作系统篇

393 阅读12分钟

操作系统概述

操作系统的特征

1、并发性: 是在计算机系统中同时存在多个程序,宏观上看,这些程序是同时向前推进的。 在单CPU上,这些并发执行的程序是交替在CPU上运行的。 程序并发性体现在两个方面: 用户程序与用户程序之间的并发执行。 用户程序与操作系统程序之间的并发。 2、共享性: 资源共享是操作系统程序和多个用户程序共用系统中的资源。 3、虚拟: 是指通过技术将一个物理实体变成若干个逻辑上的对应物。 4、异步: 即不确定性。同一程序和数据的多次运行可能得到不同的结果

操作系统的功能

1.操作系统作为计算机系统资源的管理者:

  • 进程管理(处理机管理):进程控制、进程同步、进程通信、死锁处理、处理机调度
  • 内存管理(存储器管理)
  • 文件管理
  • 设备管理:完成用户的I/O请求

2.提供用户接口:程序接口(如API)和用户接口(如GUI) 3.操作系统用作扩充机器

进程管理

进程与线程的区别和联系

  • 进程是程序的动态执行过程,是资源分配的基本单位;线程是进程中的一个实体,是被系统独立调度和分派的基本单位。线程是程序执行的基本单位,是轻量级的进程。
  • 进程是拥有资源的基本单位,而线程不拥有系统资源,所以切换线程的时空开销小。
  • 不仅进程之间可以并发执行,线程之间也可以并发执行,从而使操作系统具有更好的并发性,提高了系统的吞吐量。
  • 创建和撤销进程时的系统开销远大于创建和撤销线程时的系统开销。
  • 相比于线程,进程之间是不共享资源和地址空间的,所以不会存在太多的安全问题。
  • 同一个进程可以包含多个线程,一个进程中至少包含一个线程,一个线程只能存在于一个进程中。
  • 进程结束后,该进程下的所有线程将销毁,而一个线程的结束不会影响同一进程下的其他线程。
  • 多进程:操作系统中同时运行的多个程序;多线程:在同一个进程中同时运行的多个任务

协程,又称微线程,纤程。英文名Coroutine。是一种用户态的轻量级线程,线程内部调度的基本单位,协程的调度完全由用户控制(进程和线程都是由cpu 内核进行调度)。

例子:如果把计算机的操作系统比作一个大的工厂,那么进程就是这个工厂中的各个相互独立的车间,线程指的是车间中的流水线工人。每个车间中至少有一个工人,一个车间也也可以有多个工人,他们共享这个车间中的所有资源。也就是说,一个进程中可以有多个线程,但一个进程中至少有一个线程,他们共享这个进程下的所有资源。

进程调度算法

1、 先来先服务调度算法 first-come first-serverd(FCFS)

非抢占式的调度算法,按照请求的顺序进行调度。

有利于长作业,但不利于短作业,因为短作业必须一直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成了短作业等待时间过长。

2、 短作业优先调度算法 shortest job first(SJF)

非抢占式的调度算法,按估计运行时间最短的顺序进行调度。

长作业有可能会饿死,处于一直等待短作业执行完毕的状态。因为如果一直有短作业到来,那么长作业永远得不到调度。

3、最短剩余时间优先调度算法 shortest remaining time next(SRTN)

短作业优先的抢占式版本,按剩余运行时间的顺序进行调度。 当一个新的作业到达时,其整个运行时间与当前进程的剩余时间作比较。

如果新的进程需要的时间更少,则挂起当前进程,运行新的进程。否则新的进程等待。

4、时间片轮转调度算法(RR)

抢占式,将所有就绪进程按 FCFS 的原则排成一个队列,每次调度时,把 CPU 时间分配给队首进程,该进程可以执行一个时间片。

当时间片用完时,由计时器发出时钟中断,调度程序便停止该进程的执行,并将它送往就绪队列的末尾,同时继续把 CPU 时间分配给队首的进程。

时间片轮转算法的效率和时间片的大小有很大关系:

因为进程切换都要保存进程的信息并且载入新进程的信息,如果时间片太小,会导致进程切换得太频繁,在进程切换上就会花过多时间。 而如果时间片过长,那么实时性就不能得到保证。

5、优先级调度算法

为每个进程分配一个优先级,按优先级进行调度,有抢占式和非抢占式

为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级。

6、高响应比优先调度算法(HRRN) 非抢占式,响应比=(等待时间+运行时间)/ 运行时间

7、多级反馈队列调度算法

抢占式,多级队列是为需要连续执行多个时间片的进程考虑,它设置了多个队列,每个队列时间片大小都不同,例如 1,2,4,8,..。进程在第一个队列没执行完,就会被移到下一个队列。

这种方式下,之前的进程只需要交换 7 次。每个队列优先权也不同,最上面的优先权最高。因此只有上一个队列没有进程在排队,才能调度当前队列上的进程。

可以将这种调度算法看成是时间片轮转调度算法和优先级调度算法的结合。

进程间通信方式

1、共享存储 共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。 2、消息队列 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。 3、管道 管道是一种半双工的通信方式,即某一时刻只能单向传输。 分为无名管道(只能在具有亲缘关系的进程间使用)、高级管道( 将另一个程序当做一个新的进程在当前程序进程中启动,则它算是当前程序的子进程)、有名管道(但是它允许无亲缘关系进程间的通信) 4、信号量 信号量是一个计数器,可以用来控制多个进程对共享资源的访问,它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。 5、信号 用于通知接受进程某个事件已经发生。 6、套接字 适用于不同机器间进程通信,在本地也可作为两个进程通信的方式。

死锁

概念: 死锁是指多个进程因竞争资源而造成的一种互相等待的僵局,每个进程持有某种资源而又等待其它进程释放它或它们现在保持着的资源,若无外力作用,这些进程都无法向前推进。 死锁产生的必要条件: 1.互斥条件 2.不可剥夺条件 3.请求并保持条件:占有且等待 4.循环等待条件 死锁的处理策略: 1.预防死锁:破坏死锁的四个必要条件之一即可 2.避免死锁:银行家算法 3.检测死锁 4.解除死锁:资源剥夺法、撤销进程法、进程回退法

内存管理

动态分区分配算法

在这里插入图片描述 其中, 最坏适应算法, 为了解决最佳适应算法的问题—即留下太多难以利用的小碎片,可以在每次分配时优先使用最大的连续空闲区,这样分配后剩余的空闲区就不会太小,更方便使用。 邻近适应算法: 算法思想:首次适应算法每次都从链头开始查找的。这可能会导致低地址部分出现很多小的空闲分区,而每次分配查找时,都要经过这些分区,因此也增加了查找的开销。如果每次都从上次查找结束的位置开始检索,就能解决上述问题。

如何实现:空闲分区以地址递增的顺序排列(可排成-一个循环链表)。每次分配内存时从上次查找结束的位置开始查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区。

总结: 首次适应不仅最简单,通常也是最好最快,不过首次适应算法会使得内存低地址部分出现很多小的空闲分区,而每次查找都要经过这些分区,因此也增加了查找的开销。邻近算法试图解决这个问题,但实际上,它常常会导致在内存的末尾分配空间分裂成小的碎片,它通常比首次适应算法结果要差。

最佳导致大量碎片,最坏导致没有大的空间。 首次适应比最佳适应要好,他们都比最坏好。 在这里插入图片描述 在这里插入图片描述

覆盖和交换

覆盖和交换技术是在多道程序环境下用来扩充内存的两种方法。 交换技术主要是在不同进程(或作业)之间进行,而覆盖则用于同一程序或进程中。 现代操作系统采用虚拟内存技术,覆盖技术已成为历史,而交换技术则在现代操作系统中仍具有较强的生命力。

虚拟技术

虚拟技术把一个物理实体转换为多个逻辑实体。

主要有两种虚拟技术:时(时间)分复用技术和空(空间)分复用技术

多进程与多线程:多个进程能在同一个处理器上并发执行使用了时分复用技术,让每个进程轮流占用处理器,每次只执行一小个时间片并快速切换。

虚拟内存使用了空分复用技术,它将物理内存抽象为地址空间,每个进程都有各自的地址空间。地址空间的页被映射到物理内存,地址空间的页并不需要全部在物理内存中,当使用到一个没有在物理内存的页时,执行页面置换算法,将该页置换到内存中。

如果系统中具有快表后,那么地址的转换过程变成什么样了?

①CPU给出逻辑地址,由某个硬件算得页号、页内偏移量,将页号与快表中的所有页号进行比较。

②如果找到匹配的页号,说明要访问的页表项在快表中有副本,则直接从中取出该页对应的内存块号,再将内存块号与页内偏移量拼接形成物理地址,最后,访问该物理地址对应的内存单元。因此,若快表命中,则访问某个逻辑地址仅需一次访存即可。

③如果没有找到匹配的页号,则需要访问内存中的页表,找到对应页表项,得到页面存放的内存块号,再将内存块号与页内偏移量拼接形成物理地址,最后,访问该物理地址对应的内存单元。因此,若快表未命中,则访问某个逻辑地址需要两次访存(注意:在找到页表项后,应同时将其存入快表,以便后面可能的再次访问。但若快表已满,则必须按照-定的算法对旧的页表项进行替换)

由于查询快表的速度比查询页表的速度快很多,因此只要快表命中,就可以节省很多时间。 因为局部性原理,–般来说快表的命中率可以达到90%以上。

例:某系统使用基本分页存储管理,并采用了具有快表的地址变换机构。访问- -次快表耗时1us, 访问一次内存耗时100us。若快表的命中率为90%,那么访问一个逻辑地址的平均耗时是多少? (1+100) * 0.9 + (1+100+100) * 0.1 = 111 us 有的系统支持快表和慢表同时查找,如果是这样,平均耗时应该是(1+100) * 0.9+ (100+100) *0.1=110.9 us 若未采用快表机制,则访问一个逻辑地址需要100+100 = 200us 显然,引入快表机制后,访问一个逻辑地址的速度快多了。 此处参考