【面试总结】操作系统

大内核和微内核

1. 大内核

大内核是将操作系统功能作为一个紧密结合的整体放到内核。

由于各模块共享信息,因此有很高的性能。

2. 微内核

由于操作系统不断复杂,因此将一部分操作系统功能移出内核,从而降低内核的复杂性。移出的部分根据分层的原则划分成若干服务,相互独立。

在微内核结构下,操作系统被划分成小的、定义良好的模块,只有微内核这一个模块运行在内核态,其余模块运行在用户态。

因为需要频繁地在用户态和核心态之间进行切换,所以会有一定的性能损失。

 

进程与线程:

  • 进程:系统分配资源的基本单位,拥有自己独立的地址空间和数据段。多进程情况下,资源开销大,一个进程可以拥有多个线程。进程与进程之间是独立的,一个进程消失不会影响另外的进程。有复杂的通信机制,管道,消息队列,socket等。
  • 线程:CPU执行任务的基本单位,同一进程中所有线程共享数据段,拥有自己独立的栈空间。开销比进程小,切换线程速度比进程快。通信机制,由于共享数据段,较为简单

进程

进程是资源分配的基本单位。

进程控制块 (Process Control Block, PCB) 描述进程的基本信息和运行状态,所谓的创建进程和撤销进程,都是指对 PCB 的操作。

下图显示了 4 个程序创建了 4 个进程,这 4 个进程可以并发地执行。

线程

线程是独立调度的基本单位。

扫描二维码关注公众号,回复: 3069177 查看本文章

一个进程中可以有多个线程,它们共享进程资源。

QQ 和浏览器是两个进程,浏览器进程里面有很多线程,例如 HTTP 请求线程、事件响应线程、渲染线程等等,线程的并发执行使得在浏览器中点击一个新链接从而发起 HTTP 请求时,浏览器还可以响应用户的其它事件。

区别

(一)拥有资源

进程是资源分配的基本单位,但是线程不拥有资源,线程可以访问隶属进程的资源。

(二)调度

线程是独立调度的基本单位,在同一进程中,线程的切换不会引起进程切换,从一个进程内的线程切换到另一个进程中的线程时,会引起进程切换。

(三)系统开销

由于创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、I/O 设备等,所付出的开销远大于创建或撤销线程时的开销。类似地,在进行进程切换时,涉及当前执行进程 CPU 环境的保存及新调度进程 CPU 环境的设置,而线程切换时只需保存和设置少量寄存器内容,开销很小。

(四)通信方面

进程间通信 (IPC) 需要进程同步和互斥手段的辅助,以保证数据的一致性。而线程间可以通过直接读/写同一进程中的数据段(如全局变量)来进行通信。

死锁及处理方法:

死锁的四个必要条件:

  • 互斥条件:同一资源在同一时间只能被一个进程所占有。其他进程无法占用
  • 不可抢占条件:某资源被进程占有时不能被其他进程抢占,除非自己释放。
  • 请求和占有:进程在占有资源的同时也可以对新的资源发起请求
  • 循环等待:存在一种循环等待链,即进程在占有该资源的同时又被另外一个进程所等待,形成一个环形链。

预防死锁的方法:

由于资源互斥是资源的固有属性,无法破坏,只能破坏死锁的三个必要条件来预防:

  • 破坏不可抢占:当某进程已经获得资源且无法获得新申请的全部资源时,释放掉现有资源,需要时重新申请。
  • 破坏请求与占有条件:即一次性分配给进程所需所有资源,即不会再提出新的请求。若无法分配就不给这个进程分配资源,解决了占有条件。
  • 破坏循环等待条件:将资源进行编号,进程只有申请了序号小的资源后,才能申请大的资源。

死锁避免

单个资源的银行家算法

一个小城镇的银行家,他向一群客户分别承诺了一定的贷款额度,算法要做的是判断对请求的满足是否会进入不安全状态,如果是,就拒绝请求;否则予以分配。

上图 c 为不安全状态,因此算法会拒绝之前的请求,从而避免进入图 c 中的状态。

(三)多个资源的银行家算法

上图中有五个进程,四个资源。左边的图表示已经分配的资源,右边的图表示还需要分配的资源。最右边的 E、P 以及 A 分别表示:总资源、已分配资源以及可用资源,注意这三个为向量,而不是具体数值,例如 A=(1020),表示 4 个资源分别还剩下 1/0/2/0。

操作系统的中断分类

  • 外中断:由CPU执行指令之外的因素造成的中断。如IO完成,完成输入/输出,CPU可以再次调用IO设备,时钟中断,控制台中断。
  • 内中断(异常):由CPU指令造成的异常中断。如:非法操作码,地址越界,算法溢出。
  • 陷入:在用户程序中进行系统调用。

进程调度算法:

  • 先来先服务算法FCFS(first come first serve)
  1. 算法原理:按照作业提交或者进程进入就绪状态的顺序进行分配CPU,若交出CPU后或重新排队,需要再执行,需要重新排队在队尾。(不可抢占式调度)
  2. 算法优点:算法实现简单,容易理解。只要一个队列 FIFO就可以实现
  3. 算法缺点:利于长进程运行,不利于短进程运行。
  • 短进程优先算法SJF(shortest job first )
  1. 算法原理:当前排队的队列中,找到执行时间最短的作业或者进程,让其优先运行。(通常不会抢占正在执行的进程)
  2. 算法优点:相比FCFS,提高进程平均周转时间,缩短进程等待时间,提高了吞吐量。
  3. 算法缺点:对于长进程不友好,可能导致长进程长时间无法获得CPU,且未能根据进程的紧迫程度进行CPU调度,估计执行时间也不一定准确,从而影响调度性能。
  • 高优先权优先算法(FPF)
  1. 算法原理:从当前就绪队列中,选取优先权最高的进程优先执行。可分为:可抢占式和不可抢占式。可抢占式表示出现比正在执行的进程优先级更高的进程,中断当前进程,运行更高优先级的进程。不可抢占表示,等待当前进程运行结束,运行等待进程。
  2. 算法优点:非抢占式:这种算法一般用于批处理,对实时性要求不高的系统。抢占式用于实时性要求较高的批处理系统或者分时系统中。
  3. 算法缺点:
  • 高响应比优先调度算法HRRN(highest response ratio next)
  1. 算法原理:同时考虑进程的等待时间长短和执行时间,计算出最高响应时间比优先执行。R = (w+t)/t=1+w/t,w等待时间,t执行时间。
  2. 算法优点:使得长作业也有机会投入执行,但是相比SJF处理的作业数量要少,所以吞吐量比不上SJF
  3. 算法缺点:每次调度之前要进行响应比计算,增加系统开销。
  • 基于时间片的轮转调度算法
  1. 算法原理:将所有就绪进程利用FCFS排成一个队列,然后将给予队首的进程,分配一个时间片进入执行,一旦时间片时间到了,利用时钟中断,将进程重新放回FCFS的队尾,运行下一个队首的进程。若一个时间片未运行结束,也可以阻塞。
  2. 算法优点:简单易行,平均响应时间短
  3. 算法缺点:不利于处理紧急作业。该算法中,时间片大小影响系统性能。
  • 时间片大小的确定:
  • 系统对响应时间的要求
  • 队列的长度大小
  • 系统的处理能力
  • 多级反馈队列调度算法
  1. 算法原理:
  • 分配多个FCFS队列存放进程,按照编号分配优先级Q1优先级最高,依次递减
  • 每级队列中采用时间片轮转算法,若进程在Q1时间片内未结束,则进入Q2队列,依次向下存储
  • 优先执行优先级高的队列,直到队列中的进程执行完毕,执行下一级队列
  • 若正在执行低优先级进程时,有高优先级进程进入,那么采用抢占式,将该进程放在队列末尾,转而执行高优先级进程。
  1. 算法优点:动态调整算法,为了系统吞吐量和平均周转时间照顾短进程,同时照顾优先级进程。
  2. 从算法来看:长进程无法长期占用处理机,且系统的响应时间会缩短,吞吐量也不错(前提是没有频繁的短进程)。所以MLFQ调度算法是一种合适不同类型应用特征的综合进程调度算法。

实时系统

实时系统要求一个请求在一个确定时间内得到响应。

分为硬实时和软实时,前者必须满足绝对的截止时间,后者可以容忍一定的超时。

信号量

用于进程之间的同步和互斥操作。一般利用P/V操作对信号量进行操作。

(P 通过)down:当信号量大于0时,进行减一操作。当信号量等于,进程睡眠,等待信号量大于0;

(V 释放)up:当信号量被进程释放,进行加一操作。此时有进程睡眠,可以唤醒进程,进入执行并减一操作。

PV操作由P操作原语和V操作原语组成(原语是不可中断的过程)。

当需要互斥同步操作时,将信号量初值置为1即可。

如果信号量的取值只能为 0 或者 1,那么就成为了互斥量(Mutex),0 表示临界区已经加锁,1 表示临界区解锁。

typedef int semaphore;
semaphore mutex = 1;
void P1() {
    down(&mutex);
    // 临界区
    up(&mutex);
}

void P2() {
    down(&mutex);
    // 临界区
    up(&mutex);
}

使用信号量实现生产者-消费者问题 

问题描述:使用一个缓冲区来保存物品,只有缓冲区没有满,生产者才可以放入物品;只有缓冲区不为空,消费者才可以拿走物品。

因为缓冲区属于临界资源,因此需要使用一个互斥量 mutex 来控制对缓冲区的互斥访问。

为了同步生产者和消费者的行为,需要记录缓冲区中物品的数量。数量可以使用信号量来进行统计,这里需要使用两个信号量:empty 记录空缓冲区的数量,full 记录满缓冲区的数量。其中,empty 信号量是在生产者进程中使用,当 empty 不为 0 时,生产者才可以放入物品;full 信号量是在消费者进程中使用,当 full 信号量不为 0 时,消费者才可以取走物品。

注意,不能先对缓冲区进行加锁,再测试信号量。也就是说,不能先执行 down(mutex) 再执行 down(empty)。如果这么做了,那么可能会出现这种情况:生产者对缓冲区加锁后,执行 down(empty) 操作,发现 empty = 0,此时生产者睡眠。消费者不能进入临界区,因为生产者对缓冲区加锁了,消费者就无法执行 up(empty) 操作,empty 永远都为 0,导致生产者永远等待下,不会释放锁,消费者因此也会永远等待下去。

#define N 100
typedef int semaphore;
semaphore mutex = 1;
semaphore empty = N;
semaphore full = 0;

void producer() {
    while(TRUE) {
        int item = produce_item();
        down(&empty);
        down(&mutex);
        insert_item(item);
        up(&mutex);
        up(&full);
    }
}

void consumer() {
    while(TRUE) {
        down(&full);
        down(&mutex);
        int item = remove_item();
        up(&mutex);
        up(&empty);
        consume_item(item);
    }
}

进程间的通信

1. 管道

管道是通过调用 pipe 函数创建的,fd[0] 用于读,fd[1] 用于写。

#include <unistd.h>
int pipe(int fd[2]);

它具有以下限制:

  • 只支持半双工通信(单向交替传输);
  • 只能在父子进程中使用。

2. FIFO

也称为命名管道,去除了管道只能在父子进程中使用的限制。

#include <sys/stat.h>
int mkfifo(const char *path, mode_t mode);
int mkfifoat(int fd, const char *path, mode_t mode);

FIFO 常用于客户-服务器应用程序中,FIFO 用作汇聚点,在客户进程和服务器进程之间传递数据。

3. 消息队列

相比于 FIFO,消息队列具有以下优点:

  • 消息队列可以独立于读写进程存在,从而避免了 FIFO 中同步管道的打开和关闭时可能产生的困难;
  • 避免了 FIFO 的同步阻塞问题,不需要进程自己提供同步方法;
  • 读进程可以根据消息类型有选择地接收消息,而不像 FIFO 那样只能默认地接收。
  • 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

4. 信号量

它是一个计数器,用于为多个进程提供对共享数据对象的访问。

5. 共享存储

允许多个进程共享一个给定的存储区。因为数据不需要在进程之间复制,所以这是最快的一种 IPC。

需要使用信号量用来同步对共享存储的访问。

多个进程可以将同一个文件映射到它们的地址空间从而实现共享内存。另外 XSI 共享内存不是使用文件,而是使用使用内存的匿名段。

6. 套接字

与其它通信机制不同的是,它可用于不同机器间的进程通信。

内存管理

虚拟内存

虚拟内存的目的是为了让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。

为了更好的管理内存,操作系统将内存抽象成地址空间。每个程序拥有自己的地址空间,这个地址空间被分割成多个块,每一块称为一页。这些页被映射到物理内存,但不需要映射到连续的物理内存,也不需要所有页都必须在物理内存中。当程序引用到不在物理内存中的页时,由硬件执行必要的映射,将缺失的部分装入物理内存并重新执行失败的指令。

从上面的描述中可以看出,虚拟内存允许程序不用将地址空间中的每一页都映射到物理内存,也就是说一个程序不需要全部调入内存就可以运行,这使得有限的内存运行大程序称为可能。例如有一台计算机可以产生 16 位地址,那么一个程序的地址空间范围是 0~64K。该计算机只有 32KB 的物理内存,虚拟内存技术允许该计算机运行一个 64K 大小的程序。

分页系统地址映射

内存管理单元(MMU)管理着地址空间和物理内存的转换,其中的页表(Page table)存储着页(程序地址空间)和页框(物理内存空间)的映射表。

下图的页表存放着 16 个页,这 16 个页需要用 4 个比特位来进行索引定位,也就是存储页面号,剩下 12 个比特位存储偏移量。

例如对于虚拟地址(0010 000000000100),前 4 位是存储页面号 2,读取表项内容为(110 1)。该页在内存中,并且页框的地址为 (110 000000000100)。

页面置换算法

当程序运行时,访问的页面不在内存中,发生缺页中断,将需要的页面调入主存,发现主存中内存不足,此时需要将主存中页面放入磁盘对换区腾出空间。

页面置换算法目的:使得页面置换的频率发生最低。

  • 最佳置换算法(OPT):

  • 选择最长时间不再被使用的页面作为置换页面,通常保证置换页面频率最低。
  • 是一种理论上的算法。因为无法保证总是知道,页面的最长时间不被访问。
  • 该算法所选择淘汰的页面为后面永远不会使用到的页面或者是最长时间内不会使用的页面。但由于我们无法预知接下来执行的过程中哪些页面不会被访问到,所以这个算法无法实现,但是最佳页面置换算法可以用于对可实现算法的性能进行衡量比较。
  • 举例:一个系统为某进程分配了三个物理块,并有如下页面引用序列:
  • 开始运行时,先将 7, 0, 1 三个页面装入内存。当进程要访问页面 2 时,产生缺页中断,会将页面 7 换出,因为页面 7 再次被访问的时间最长。
  • 先进先出算法FIFO

  • 方法:总是置换出停留在主存中时间最长的页面,也就是最先进来的页面先被置换出去。
  • 缺点:有可能将经常使用的页面置换出去,从而使得缺页率上升。
  • 最近最久未使用LRU(least recently used)

  • 该算法:认为过去一段时间内最久未被使用的页面,将来也将很少使用,将置换过去一段时间内最久未被使用的页面。
  • 维护一个链表,若有页面使用,将页面放在链表的表头,这样只要页面最近一段时间未被访问,一定出现在表尾。
  • 因实现LRU算法必须有大量硬件支持,还需要一定的软件开销。所以实际实现的都是一种简单有效的LRU近似算法。 
  • 因为每次访问都需要更新链表,因此这种方式实现的 LRU 代价很高。

     

  • 最近未使用

    NRU, Not Recently Used

    每个页面都有两个状态位:R 与 M,当页面被访问时设置页面的 R=1,当页面被修改时设置 M=1。其中 R 位会定时被清零。可以将页面分成以下四类:

  • R=0,M=0
  • R=0,M=1
  • R=1,M=0
  • R=1,M=1
  • 当发生缺页中断时,NRU 算法随机地从类编号最小的非空类中挑选一个页面将它换出。

    NRU 优先换出已经被修改的脏页面(R=0,M=1),而不是被频繁使用的干净页面(R=1,M=0)。

  • 最少使用算法(LFU):

  • 该算法首先淘汰一段时间内使用次数最少的页面,注意和LRU的区别,LRU是淘汰最长时间没使用的在采用最少使用置换算法时,应为在内存中的每个页面设置一个移位寄存器,用来记录该页面被访问的频率。该置换算法选择在之前时期使用最少的页面作为淘汰页。

磁盘结构

  • 盘面(Platter):一个磁盘有多个盘面;
  • 磁道(Track):盘面上的圆形带状区域,一个盘面可以有多个磁道;
  • 扇区(Track Sector):磁道上的一个弧段,一个磁道可以有多个扇区,它是最小的物理储存单位,目前主要有 512 bytes 与 4 K 两种大小;
  • 磁头(Head):与盘面非常接近,能够将盘面上的磁场转换为电信号(读),或者将电信号转换为盘面的磁场(写);
  • 制动手臂(Actuator arm):用于在磁道之间移动磁头;
  • 主轴(Spindle):使整个盘面转动。

磁盘调度算法

读写一个磁盘块的时间的影响因素有:

  • 旋转时间(主轴转动盘面,使得磁头移动到适当的扇区上)
  • 寻道时间(制动手臂移动,使得磁头移动到适当的磁道上)
  • 实际的数据传输时间

其中,寻道时间最长,因此磁盘调度的主要目标是使磁盘的平均寻道时间最短。

  • 先来先服务FCFS

  • 根据磁盘请求的顺序执行调度
  • 优点:公平且简单
  • 缺点:因为未做任何优化,平均寻道时间没有改变。可能较长。
  • 最短寻道时间优先SSTF, Shortest Seek Time First

  • 根据距离当前磁头位置最短的磁道请求进行磁盘调度
  • 优点:平均磁道请求时间段
  • 缺点:容易出现饥饿现象,若新进的磁道请求总是距离磁头最近,那么两边的磁道请求一直处于等待。

  • 电梯算法

  • 总是保持一个方向上运行调度,直到这个方向上没有磁道请求,然后改变方向
  • 类似于扫描算法,跟电梯的运行规律很像
  • 因为考虑了方向的问题,因此所有的磁道请求都会被满足,不会出现SSTF的饥饿问题。

 

猜你喜欢

转载自blog.csdn.net/weixin_38035852/article/details/81591097