操作系统面试题

进程和线程

  1. 进程是系统进行资源调度和分配的基本单位。

  2. 线程是进程的子任务,是CPU调度和分派的基本单位。

  3. 一个程序至少有一个进程,一个进程至少
    有一个线程,线程依赖进程的存在。

  4. 进程执行过程中拥有独立的内存单元,
    而多个线程共享进程的内存。

  5. 线程自己基本上不拥有系统资源,
    只拥有一点在运行中必不可少的资源
    (如程序计数器,一组寄存器和栈)

程序计数器的作用

PC寄存器是用来存储指向下一条指令的地址,
也即将将要执行的指令代码。
由执行引擎读取下一条指令。

进程间的通信的几种方式

  1. 管道(pipe)及命名管道(named pipe):
    其实就是在内存中开辟的一块大小固定的缓冲区,
    半双工通信,管道可用于具有亲缘关系的父子进程间的通信,
    有名管道除了具有管道所具有的功能外,
    它还允许无亲缘关系进程间的通信,
    写满时不能再写,读空时不能再读,
    没写满时不能读,没读空时不能写;

  2. 信号(signal):
    信号用于通知接收进程某个事件已经发生;
    可以在任何时候发给某一进程,
    而无需知道该进程的状态

  3. 消息队列: 消息队列是消息的链接表,
    它克服了上两种通信方式中信号量有限的缺点,
    具有写权限得进程可以按照一定得规则
    向消息队列中添加新信息;
    对消息队列有读权限得进程则可以从消息队列中读取信息;

  4. 共享内存:
    可以说这是最有用的进程间通信方式。
    它使得多个进程可以访问同一块内存空间,
    不同进程可以及时看到对方进程中
    对共享内存中数据得更新。
    这种方式需要依靠某种同步操作,如互斥锁和信号量等;

  5. 信号量: 主要作为进程之间及同一种进程的
    不同线程之间得同步和互斥手段;

  6. 套接字: 这是一种更为一般得进程间通信机制,
    它可用于网络中不同机器之间的进程间通信,
    应用非常广泛。

几种方式的比较:

  • 管道:速度慢、容量有限
  • 消息队列:容量收到系统限制,
    且要注意第一次读的时候,
    要考虑上一次没有读完数据的问题。
  • 信号量:不能传递复杂信息,只能用来同步。
  • 共享内存:能够很容易控制容量,速度快,但要保持同步,
    比如一个进程在写的时候,另一个进程要注意读写的问题
    相当于线程中的线程安全。

线程间通信

(1)同步
多个线程通过synchronized通讯,类似于共享内存

(2)while轮询
线程A不断改变条件,线程B不断查看条件是否满足需求
从而实现通讯。
效率不高,因为B一直在查看,没做别的

(3)wait/notify
进入阻塞,而不是像轮询一样一直占用CPU资源

(4)管道通信
通过管道,将一个线程的消息发送个另一个线程

什么是死锁?死锁产生的条件?

1). 死锁的概念
  在两个或者多个并发进程中,如果每个进程持有某种资源
  而又等待其它进程释放它或它们现在保持着的资源,
  在未改变这种状态之前都不能向前推进,
  称这一组进程产生了死锁。
  通俗的讲,就是两个或多个进程
  无限期的阻塞、相互等待的一种状态。
2). 死锁产生的四个必要条件

互斥:至少有一个资源必须属于非共享模式,
即一次只能被一个进程使用;若其他申请使用该资源
,那么申请进程必须等到该资源被释放为止;

占有并等待:一个进程必须占有至少一个资源,
并等待另一个资源,而该资源为其他进程所占有;

非抢占:进程不能被抢占,
即资源只能被进程在完成任务后自愿释放

循环等待:若干进程之间形成一种头尾相接的环形等待资源关系

死锁的处理基本策略和常用方法

  1. 解决死锁的基本方法主要有
  2. 预防死锁、避免死锁、检测死锁、解除死锁 、鸵鸟策略 等。
1.死锁预防 :

只要确保死锁发生的四个必要条件中至少有一个不成立

  • 打破互斥条件:允许进程同时访问某些资源。
    但是,有些资源是不能被多个进程所共享的,
    这是由资源本身属性所决定的,
    因此,这种办法通常并无实用价值。

  • 打破占有并等待条件:可以实行资源预先分配策略
    (进程在运行前一次性向系统申请它所需要的全部资源,
    若所需全部资源得不到满足,则不分配任何资源,
    此进程暂不运行;只有当系统能满足当前进程所需的全部资源时,
    才一次性将所申请资源全部分配给该线程)
    或者只允许进程在没有占用资源时才可以申请资源
    (一个进程可申请一些资源并使用它们,
    但是在当前进程申请更多资源之前,
    它必须全部释放当前所占有的资源)
    但是这种策略也存在一些缺点:
    在很多情况下,无法预知一个进程执行前所需的全部资源,
    因为进程是动态执行的,不可预知的;
    同时,会降低资源利用率,导致降低了进程的并发性。

  • 打破非抢占条件:允许进程强行从占有者哪里夺取某些资源。
    也就是说,但一个进程占有了一部分资源,
    在其申请新的资源且得不到满足时,
    它必须释放所有占有的资源以便让其它线程使用。
    这种预防死锁的方式实现起来困难,会降低系统性能。

  • 打破循环等待条件:实行资源有序分配策略。
    对所有资源排序编号,所有进程对资源的请求
    必须严格按资源序号递增的顺序提出,
    即只有占用了小号资源才能申请大号资源,
    这样就不回产生环路,预防死锁的发生。

2.死锁避免 :

死锁避免的基本思想是动态地检测资源分配状态,
以确保循环等待条件不成立,从而确保系统处于安全状态。
所谓安全状态是指:如果系统能按某个顺序为每个进程分配资源
不超过其最大值),那么系统状态是安全的,换句话说就是,如果存在一个安全序列,那么系统处于安全状态。
资源分配图算法和银行家算法是两种经典的死锁避免的算法,
其可以确保系统始终处于安全状态。
其中,资源分配图算法应用场景为每种资源类型只有一个实例
(申请边,分配边,需求边,不形成环才允许分配),
而银行家算法应用于每种资源类型可以有多个实例的场景。

3.死锁解除

死锁解除的常用两种方法为进程终止和资源抢占。
所谓进程终止是指简单地终止一个或多个进程以打破循环等待,
包括两种方式:终止所有死锁进程和一次只终止一个进程直到取消死锁循环为止;
所谓资源抢占是指从一个或多个死锁进程那里抢占一个或多个资源,
此时必须考虑三个问题:
(I). 选择一个牺牲品
(II). 回滚:回滚到安全状态
(III). 饥饿(在代价因素中加上回滚次数,回滚的越多则越不可能继续被作为牺牲品,避免一个进程总是被回滚)

银行家算法

在这里插入图片描述

【银行家算法】

数据结构
  1. 可利用资源向量Available。
    这是一个含有m个元素的数组,
    其中的而每一个元素代表一类可利用资源数目,
    其初始值是系统中所配置的该类全部可用资源的数目,
    其数值随该类资源的分配和回收而动态的改变。

  2. 最大需求矩阵Max。这是一个n*m的矩阵,
    它定义了系统中n个进程中的每一个进程对m类资源的最大需求。
    如果Max[i,j]=K;则表示进程i需要Rj类资源的最大数目为K。

  3. 分配矩阵Allocation。
    这也是一个n*m的矩阵,
    它定义了系统中每一类资源当前已分配给每一进程的资源数。
    如果Allocation[i,j]=K,
    则表示进程i当前已分得Rj类资源的数目为K。

  4. 需求矩阵Need。
    这也是一个n*m的矩阵,
    用以表示每一个进程尚需的各类资源数。
    如果Need[i,j]=K,则表示进程i还需要Rj类资源K个,方能完成任务。

上述三个矩阵间存在下述关系:Need[i,j]=Max[i,j]-Allocation[i,j]

  1. 系统执行安全性算法,检查此次资源分配后系统是否处于安全状态。
    若安全,才正式将资源分配给进程Pi,以完成本次分配;
    否则,将本次的试探分配作废,恢复原来的资源分配状态
    ,让进程Pi等待。
安全性算法模块
  1. 设置两个向量

① 工作向量:Work=Available
(表示系统可提供给进程继续运行所需要的各类资源数目)

② Finish:表示系统是否有足够资源分配给进程
(True:有;False:没有).初始化为False

  1. 若Finish[i]=False&&Need<=Work,则执行3;否则执行4(i为资源类别)

  2. 进程P获得第i类资源,则顺利执行直至完成,并释放资源: Work=Work+Allocation; Finish[i]=true;转2

  3. 若所有进程的Finish[i]=true,则表示系统安全;否则,不安全!

思想

允许进程动态地申请资源,系统在每次实施资源分配之前,
先计算资源分配的安全性,
若此次资源分配安全(
即资源分配后,系统能按某种顺序来为每个进程分配其所需的资源
,直至最大需求,使每个进程都可以顺利地完成)
便将资源分配给进程,否则不分配资源,让进程等待。

【安全序列】与【系统安全】

所谓系统是安全的,是指系统中的所有进程能够按照某一种次序分配资源,
并且依次地运行完毕,
这种进程序列{P1,P2,…,Pn}就是安全序列。
如果存在这样一个安全序列,则系统是安全的;
如果系统不存在这样一个安全序列,则系统是不安全的。
防止死锁的思路也就有了,
我们能不能用一个算法去找有没有安全序列来作为我们的参考,
只要系统按照这个安全序列去分配资源,
就可以预防死锁发生呢?

进程的几种状态

在这里插入图片描述

  • 创建状态:进程在创建时需要申请一个空白PCB,
    向其中填写控制和管理进程的信息,完成资源分配。
    如果创建工作无法完成,比如资源无法满足,
    就无法被调度运行,把此时进程所处状态称为创建状态

  • 就绪状态:进程已经准备好,已分配到所需资源,
    只要分配到CPU就能够立即运行

  • 执行状态:进程处于就绪状态被调度后,进程进入执行状态

  • 阻塞状态:正在执行的进程由于某些事件
    (I/O请求,申请缓存区失败)
    而暂时无法运行,进程受到阻塞。
    在满足请求时进入就绪状态等待系统调用

  • 终止状态:进程结束,或出现错误,
    或被系统终止,进入终止状态。
    无法再执行

线程有几种状态?

  1. 新建(NEW):新创建了一个线程对象。

  2. 可运行(RUNNABLE):
    线程对象创建后,其他线程(比如main线程)
    调用了该对象的start()方法。
    该状态的线程位于可运行线程池中,
    等待被线程调度选中,获取cpu 的使用权 。

  3. 运行(RUNNING):可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。

  4. 阻塞(BLOCKED):
    阻塞状态是指线程因为某种原因放弃了cpu 使用权,
    也即让出了cpu timeslice,暂时停止运行。
    直到线程进入可运行(runnable)状态,
    才有机会再次获得cpu timeslice 转到运行(running)状态。

阻塞的情况分三种:
(一). 等待阻塞:
运行(running)的线程执行o.wait()方法,
JVM会把该线程放入等待队列(waitting queue)中。
(二). 同步阻塞:
运行(running)的线程在获取对象的同步锁时,
若该同步锁被别的线程占用,
则JVM会把该线程放入锁池(lock pool)中。
(三). 其他阻塞:
运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,
或者发出了I/O请求时,JVM会把该线程置为阻塞状态。
当sleep()状态超时、join()等待线程终止或者超时、
或者I/O处理完毕时,线程重新转入可运行(runnable)状态。
5. 死亡(DEAD):线程run()、main() 方法执行结束,
6. 或者因异常退出了run()方法,则该线程结束生命周期。
7. 死亡的线程不可再次复生。

在给定的时间点上,一个线程只能处于一种状态。
在这里插入图片描述

(内存管理)分页和分段有什么区别?

段式存储管理是一种符合用户视角的内存分配管理方案。
  在段式存储管理中,将程序的地址空间划分为若干段(segment),
  如代码段,数据段,堆栈段;这样每个进程有一个二维地址空间,
  相互独立,互不干扰。
  段式管理的优点是:没有内碎片
  (因为段大小可变,改变段大小来消除内碎片)。
  内部碎片是已经被分配出去的的内存空间
  大于请求所需的内存空间
  
  但段换入换出时,会产生外碎片
  外部碎片是指还没有分配出去,
  但是由于大小太小而无法分配给
  申请空间的新进程的内存空间空闲块。
  (比如4k的段换5k的段,会产生1k的外碎片)

页式存储管理方案
  是一种用户视角内存与物理内存相分离的内存分配管理方案。
  在页式存储管理中,将程序的逻辑地址划分为固定大小的页(page),
  而物理内存划分为同样大小的帧,
  程序加载时,可以将任意一页放入内存中任意一个帧,
  这些帧不必连续,从而实现了离散分离。
  页式存储管理的优点是:
  没有外碎片(因为页的大小固定),
  但会产生内碎片(一个页可能填充不满)。

两者的不同点:

  • 目的不同:分页是由于系统管理的需要而不是用户的需要,
    它是信息的物理单位;
    分段的目的是为了能更好地满足用户的需要,
    它是信息的逻辑单位,
    它含有一组其意义相对完整的信息;

  • 大小不同:页的大小固定且由系统决定,
    而段的长度却不固定,
    由其所完成的功能决定;

  • 地址空间不同: 段向用户提供二维地址空间;
    页向用户提供的是一维地址空间;

  • 信息共享:段是信息的逻辑单位,
    便于存储保护和信息的共享,
    页的保护和共享受到限制;

  • 内存碎片:
    页式存储管理的优点是没有外碎片(因为页的大小固定),
    但会产生内碎片(一个页可能填充不满);
    而段式管理的优点是没有内碎片
    (因为段大小可变,改变段大小来消除内碎片)。
    但段换入换出时,会产生外碎片
    (比如4k的段换5k的段,会产生1k的外碎片)。

虚拟内存

虚拟内存允许执行进程不必完全在内存中。
虚拟内存的基本思想是:
每个进程拥有独立的地址空间,
这个空间被分为大小相等的多个块,
称为页(Page),
每个页都是一段连续的地址。
这些页被映射到物理内存,
但并不是所有的页都必须在内存中才能运行程序。
当程序引用到一部分不在物理内存中的地址空间时
由操作系统负责将缺失的部分装入物理内存
并重新执行。
这样,对于进程而言,
逻辑上就有很大的内存空间,
虚拟内存实际上可以比物理内存大。
如果虚拟内存的页并不存在于物理内存中
会产生缺页中断,从磁盘中取得缺的页放入内存,
如果内存已满,
还会根据某种算法将磁盘中的页换出。

页面置换算法

  • FIFO先进先出算法:在操作系统中经常被用到,
    比如作业调度(主要实现简单,很容易想到);

  • LRU(Least recently use)最近最少使用算法:
    根据使用时间到现在的长短来判断;

  • LFU(Least frequently use)最少使用次数算法:
    根据使用次数来判断;

  • OPT(Optimal replacement)最优置换算法:
    理论的最优,理论;就是要保证置换出去的是不再被使用的页,
    或者是在实际内存中最晚使用的算法。

逻辑地址、物理地址、虚拟内存

  1. 物理地址:它是地址转换的最终地址,
    进程在运行时执行指令和访问数据最后都要
    通过物理地址从主存中存取,
    是内存单元真正的地址。

  2. 逻辑地址:是指从应用程序角度看到的内存地址,又叫相对地址。
    编译后,每个目标模块都是从 0 号单元开始编址,
    称为该目标模块的相对地址或逻辑地址。
    不同进程可以有相同的逻辑地址,
    因为这些相同的逻辑地址可以映射到主存的不同位置。
    用户和程序员只需要知道逻辑地址。

  3. 虚拟内存:虚拟内存是一些系统页文件,
    存放在磁盘上,每个系统页文件大小为 4K,
    物理内存也被分页,每个页大小也为 4K,
    这样虚拟页文件和物理内存页就可以对应,
    实际上虚拟内存就是用于物理内存的临时存放的磁盘空间。
    页文件就是内存页,物理内存中每页叫物理页,
    磁盘上的页文件叫虚拟页,
    物理页+虚拟页就是系统所有使用的页文件的总和。

颠簸

颠簸本质上是指频繁的页调度行为,
具体来讲,进程发生缺页中断,这时,必须置换某一页。
然而,其他所有的页都在使用,
它置换一个页,但又立刻再次需要这个页。
因此,会不断产生缺页中断,导致整个系统的效率急剧下降,
这种现象称为颠簸(抖动)。

  • 内存颠簸的解决策略包括:

  • 如果是因为页面替换策略失误,
    可以修改替换算法来解决这个问题;

  • 如果是因为运行的程序太多,
    造成程序无法同时将所有频繁访问的页面调入内存,
    则要降低多道程序的数量;

  • 否则,还剩下两个办法:
    终止该进程或增加物理内存容量。

虚拟内存的应用与优点

虚拟内存很适合在多道程序设计系统中使用,
许多程序的片段同时保存在内存中。
当一个程序等待它的一部分读入内存时,
可以把CPU交给另一个进程使用。
虚拟内存的使用可以带来以下好处:
在内存中可以保留多个进程,
系统并发度提高

解除了用户与内存之间的紧密约束,
进程可以比内存的全部空间还大

操作系统中进程调度策略有哪几种?

  • FCFS(先来先服务,队列实现,非抢占的):
    先请求CPU的进程先分配到CPU

  • SJF(最短作业优先调度算法):
    平均等待时间最短,但难以知道下一个CPU区间长度

  • 优先级调度算法(可以是抢占的,也可以是非抢占的):
    优先级越高越先分配到CPU,相同优先级先到先服务,
    存在的主要问题是:低优先级进程无穷等待CPU,
    会导致无穷阻塞或饥饿;解决方案:老化

  • 时间片轮转调度算法(可抢占的):
    队列中没有进程被分配超过一个时间片的CPU时间,
    除非它是唯一可运行的进程。
    如果进程的CPU区间超过了一个时间片,
    那么该进程就被抢占并放回就绪队列。

  • 多级队列调度算法:
    将就绪队列分成多个独立的队列,
    每个队列都有自己的调度算法,
    队列之间采用固定优先级抢占调度。
    其中,一个进程根据自身属性被永久地分配到一个队列中。

  • 多级反馈队列调度算法:
    与多级队列调度算法相比,
    其允许进程在队列之间移动:
    若进程使用过多CPU时间,
    那么它会被转移到更低的优先级队列;
    在较低优先级队列等待时间过长的进程会被转移到更高优先级队列,
    以防止饥饿发生。

进程同步与互斥

  • 互斥:
    指某一个资源同时只允许一个访问者对其进行访问,
    具有唯一性和排它性。
    但互斥无法限制访问者对资源的访问顺序,即访问是无序的
  • 同步:是指在互斥的基础上(大多数情况下),
    通过其它机制实现访问者对资源的有序访问。
    大多数情况下,同步已经实现了互斥,
    特别是所有写入资源的情况必定是互斥的。
    少数情况是指可以允许多个访问者同时访问资源。
    同步:体现的是一种协作性。互斥:体现的是排它性。

同步、异步、阻塞、非阻塞的区别

同步和互斥
  • 同步体现的是一种协作性,互斥体现的是一种排他性

  • 同步:就是并发的线程在一些关键点上可能需要互相等待与互通信息,
    这种相互制约的等待与互通信息称为进程(线程)同步。

  • 互斥:是指某一资源同时只允许一个访问者对其进行访问,
    具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,
    即访问是无序的。

同步、异步【关注的是消息通信机制】
  • 同步: 就是指调用者会主动等待调用的返回结果。

  • 异步: 就是指调用者不会主动等待调用结果,
    而是在调用发生后,
    被调用者通过状态、通知来通知调用者。

阻塞、非阻塞 【关注的是程序在等待调用结果(消息,返回值)时的状态】
  • 阻塞: 是指调用结果返回前,当前线程会被挂起,即阻塞。

  • 非阻塞: 是指即使调用结果没返回,也不会阻塞当前线程。

进程同步有哪几种机制:

  1. 信号量机制
    一个信号量只能置一次初值,
    以后只能对之进行p操作或v操作。
    由此也可以看到,信号量机制必须有公共内存,
    不能用于分布式操作系统,这是它最大的弱点。

  2. 自旋锁

旋锁是为了保护共享资源提出的一种锁机制。
调用者申请的资源如果被占用,
即自旋锁被已经被别的执行单元保持,
则调用者一直循环在那里看是否该自旋锁的保持着已经释放了锁,
自旋锁是一种比较低级的保护数据结构和代码片段的原始方式,
可能会引起以下两个问题;
(1)死锁
(2)过多地占用CPU资源

  1. 管程
    信号量机制功能强大,但使用时对信号量的操作分散,
    而且难以控制,读写和维护都很困难。
    因此后来又提出了一种集中式同步进程——管程。
    其基本思想是将共享变量和对它们的操作集中在一个模块中,
    操作系统或并发程序就由这样的模块构成。
    这样模块之间联系清晰,便于维护和修改,易于保证正确性。

  2. 会合

进程直接进行相互作用

  1. 分布式系统

由于在分布式操作系统中没有公共内存,
因此参数全为值参,而且不可为指针。

线程同步的方式

  • 临界区:使用临界区对象。
    拥有临界区对象的线程可以访问被保护起来的资源或代码段,
    其他线程若想访问,则被挂起,
    直到拥有临界区对象的线程放弃临界区对象为止
    【只用于同一进程】

  • 互斥量 Synchronized/Lock:
    采用互斥对象机制,
    只有拥有互斥对象的线程才有访问公共资源的权限。
    因为互斥对象只有一个,
    所以可以保证公共资源不会被多个线程同时访问

  • 信号量 Semphare:它允许同一时刻多个线程访问同一资源,
    但是需要控制同一时刻访问此资源的最大线程数量
        信号量(semaphore)的数据结构为一个值和一个指针,
        指针指向等待该信号量的下一个进程。
        信号量的值 S 与相应资源的使用情况有关。
        当 S 大于 0 时,表示当前可用资源的数量;
        当 S 小于 0 时,其绝对值表示等待使用该资源的进程个数。
        注意,信号量的值仅能由 PV 操作来改变。
        执行一次 P 操作意味着请求分配一个单位资源,
        因此S的值减1;当 S < 0 时,表示已经没有可用资源,
        请求者必须等待别的进程释放该类资源,它才能运行下去。
        而执行一个 V 操作意味着释放一个单位资源,
        因此 S 的值加 1;若 S < 0,表示有某些进程正在等待该资源,
        因此要唤醒一个等待状态的进程,使之运行下去。

  • 事件(信号),Wait/Notify:
    通过通知操作的方式来保持多线程同步,
    还可以方便的实现多线程优先级的比较操作

局部性原理

(1). 时间上的局部性:最近被访问的页在不久的将来还会被访问;

(2). 空间上的局部性:内存中被访问的页周围的页也很可能被访问。

中断和轮询

  • 中断的定义
    指在计算机执行期间,
    系统内发生任何非寻常的或非预期的急需处理事件,
    使得CPU暂时中断当前正在执行的程序而
    转去执行相应的事件处理程序,
    待处理完毕后又返回原来被中断处继续执行
    或调度新的进程执行的过程

  • 轮询的定义
    定时对各种设备轮流询问一遍有无处理要求

临界区和冲突解决

  • 临界资源的定义:
    一次仅允许一个进程使用的资源

  • 临界区的定义:
    每个进程中访问临界资源的那段程序

  • 解决冲突:
    如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入
    任何时候,处于临界区内的进程不可多于一个
    进入临界区的进程要在有限时间内退出,
    以便其它进程能及时进入自己的临界区
    如果进程不能进入自己的临界区,则应让出CPU,
    避免进程出现“忙等”现象

缓冲区溢出

  • 缓冲区溢出的定义:
    指当计算机向缓冲区内填充数据时超过了缓冲区本身的容量,
    溢出的数据覆盖在合法数据上

  • 缓冲区溢出的危害:
    程序崩溃导致拒绝服务、
    跳转并且执行一段恶意代码

  • 缓冲区溢出的原因:
    程序中没有仔细检查用户输入的参数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值