1. 操作系统的四个特性

    1. 并发:同一段时间内多个程序执行
    2. 共享:系统中的资源可以被内存中多个并发执行的进线程共同使用
    3. 虚拟:通过时分复用以及空分复用技术实现把一个物理实体虚拟为多个
    4. 异步:系统中的进程以走走停停的方式执行,且以一种不可预知的速度推进
  2. 进程和线程的区别

    • 进程是具有一定功能的程序关于某个数据集合上的一次运行活动是系统进行资源调度和分配的一个独立单位
    • 线程是进程的实体,是 CPU 调度和分派的基本单位,它是比进程更小的独立运行的基本单位线程不拥有资源);
    • 一个进程可以有多个线程,多个线程也可以并发执行;
  3. 线程同步的方式有哪些

    • 互斥量(CMutex):采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权利。因为互斥对象可以保证公共资源不会被多个线程同时访问「必须是拥有互斥对象的线程」;
    • 信号量(CSemphore):它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量
    • 事件(信号):通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作
  4. 进程的通信方式有哪些

    主要分为:管道(普通管道 PIPE、流管道(s_pipe)、命名管道(name_pipe))、系统 IPC(包括消息队列、信号量、共享存储)、SOCKET

    • 管道是一种半双工的通信方式,数据只能单项流动,并且只能在具有亲缘关系的进程间流动,进程的亲缘关系通常是父子进程;
    • 命名管道是一种半双工的通信方式,它允许无亲缘关系的进程间进行通信;
    • 信号量是一个计数器,用来控制多个进程对资源的访问,通常作为一种锁机制
    • 消息队列是消息的链表,存放在内核中并由消息队列标识符标识;
    • 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生;
    • 共享内存就是映射一段能被其它进程访问的内存,这段共享内存由一个进程创建,但是多个进程可以访问;
    • 套接字SOCKET)也是一种进程间通信机制,与其它通信机制不同的是,它可用于不同进程间的进程通信
  5. 什么是缓冲区溢出?有什么危害?其原因是什么?

    缓冲区溢出(Buffer Overflow)是指当计算机向缓冲区填充数据时超出了缓冲区本身的容量,溢出的数据覆盖在合法数据上;

    危害:

    • 程序崩溃,导致拒绝额外服务;
    • 跳转并且执行一段恶意代码

    原因:造成缓冲区溢出的主要原因是程序中没有仔细检查用户输入

  6. 什么是死锁?死锁产生的四个条件?

    在两个或者多个并发进程中,如果每个进程持有某种资源而又等待其它进程释放它们持有的资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁。即两个或者多个进程因争夺资源而造成的无限期的阻塞、相互等待的一种状态

    死锁产生的四个条件(有任一条件不成立都不会产生死锁):

    • 互斥条件:一个资源一次只能被一个进程使用
    • 请求与保持条件:一个进程因请求资源而被阻塞时,又对已有的资源保持不放
    • 不剥夺条件:进程获得的资源,在未完全使用之前,不能强行剥夺
    • 循环等待条件:若干进程之间形成一种头尾相接的环形等待资源关系
  7. 进程有哪几种状态

    • 就绪状态(Ready):当进程已获得除处理机以外的所需资源,等待分配处理机资源,只要获得处理机便可立即执行
    • 运行状态(Running):已获得处理机,占用处理机资源运行,处于此状态的进程数小于等于 CPU 数
    • 阻塞状态(Blocked):进程等待某种条件,在条件满足之前无法执行,放弃处理机处于阻塞状态

    就绪→执行:处于就绪状态的进程,当进程调度程序为之分配了处理机后,就会从就绪状态变成执行状态;

    执行→就绪:处于执行状态的进程在其执行过程中,因分配给它的一个时间片已用完而就不得不让出处理机,进程从执行状态转变成就绪状态;

    执行→阻塞:正在执行的进程因等待某种事件发生而无法继续执行时,便从执行状态变成阻塞状态

    阻塞→就绪:处于阻塞状态的进程,若其等待的事件已经发生,于是进程由阻塞状态转变为就绪状态

  8. 分页和分段有什么区别

    分页:用户程序的地址空间被划分为若干固定大小的区域,称为“”。相应地,内存空间分成若干个物理块,页和块的大小相等。可将用户程序的任一页放在内存的任一块中,实现了离散分配。

    分段:将用户程序地址空间分成若干个大小不等的段,每段可以定义一组相对完整的逻辑信息。存储分配时,以段的单位,段与段在内存中可以不相连接,这样也实现了离散分配。

    区别

    • 段是信息的逻辑单位,它是根据用户的需要划分的,因此段对用户是可见的;页是信息的物理单位,是为了管理主存的方便而划分的,对用户是透明的
    • 段的大小是不固定的,由完成它的功能决定;页的大小是固定的,由系统决定
    • 段向用户提供二维地址空间;页向用户提供的是一维地址空间;
    • 段是信息的逻辑单位,便于存储保护和信息的共享;页的保护和共享受到限制

    抽象的比喻:用一个笔记本(100 页纸)去上课做笔记,课程有语数英三门课。那么记笔记的方式有两种:

    • 分段管理:从笔记本的第 1 页开始记笔记,事先规划好,比如 2~30 页做语文笔记,30~70 页做数学笔记,70~100 页做英语笔记。最后在第一张纸(第 1 页)上做个列表,记录三门课笔记的页数范围。这就是分段管理,第 1 页叫段表。
    • 分页管理:因为课是交叉上的,即这节课语文课,下节课可能是英语课。所以就有可能将各笔记连接起来记。第 2 页是语文,第 3 页是英语,第4页是数学,然后又是语文,这样交叉记笔记。然后在第 1 页做一个目录,记录语文的笔记在哪几页,数学的在哪几页。这种做法就是分页管理器,第 1 页就叫做页表
  9. 常用的页面置换算法

    • 先进先出算法:最简单粗暴的一种置换算法,没有考虑页面访问的频率信息。每次都是淘汰最早调入的页面
    • 最佳置换算法:是一种只具有理论意义的算法。策略是将当前页面中在未来最长时间内不会被访问的页面置换出去
    • 最近最久未使用置换算法(LRU):算法会赋予每个页面一个访问字段,用来记录上次页面被访问到现在所经历的时间间隔t,每次置换的时候就把 t 值最大的页面置换出去(可以通过寄存器或者栈的方式来实现)。
    • 最近未使用置换算法(NRU):也叫时钟算法 clock。页面设置一个访问位,并将页面链接为一个环形队列,页面被访问的时候访问位设置为 1。页面置换的时候,如果当前指针所指页面访问为 0,那么就置换,否则就将其置为 0,循环,直到遇到一个访问位为 0 的页面。
    • 改进型 clock 算法:在时钟算法 clock 的基础上,再添加一个修改位。替换时,根据访问位和修改位综合判断。优先替换访问位和修改位都为 0 的页面,其次是替换访问位为 0 修改位为 1 的页面。
    • 最少使用算法(LFU):设置寄存器记录页面被访问次数,每次置换的时候都置换当前访问次数最少的页面。
  10. 操作系统中进程调度策略有哪几种

    • 先来先服务调度算法:先来先服务(FCFS)调度算法是一种最简单的调度算法,该算法既可用于作业调度,也可用于进程调度。当在作业调度中采用该算法时,每次调度都是从后备作业队列中选择一个或者多个最先进入到该队列的作业,将它们掉入内存中,为它们分配资源、创建进程,然后放入就绪队列中。在进程调度中采用 FCFS 算法时,每次调度都是从就绪队列中选择一个最先进入该队列的进程,为之分配处理机,使之投入运行。该进程一直运行到完成或发生某事件而阻塞后才放弃处理机。
    • 短作业(进程)优先调度算法:短作业优(进程)先调度算法,可以分别用于作业调度和进程调度。短作业优先(SJF)调度算法是从后备队列中选择一个或若干个估计运行时间最短的作业,将它们调入内存运行;短进程优先(SPF)调度算法则是从就绪队列中选出一个估计运行时间最短的进程,将处理机分配给它,使它立即执行并一直执行到完成,或发生某事件而阻塞放弃处理机时再重新调度。
    • 高优先权优先调度算法最高优先权优先(FPF)调度算法常作用于批处理系统中,也作为多种操作系统中的进程调度算法,还可用于实时系统中。当把该算法用于作业调度时,系统会从后备队列中选择若干个优先权最高的作业装入内存。当用于进程调度时,该算法是把处理机分配给就绪队列中优先权最高的进程。
      • 非抢占式优先权算法:系统一旦把处理机分配给就绪队列中优先权最高的进程后,该进程就会一直执行下去,直到完成为止;如果因为某个事件使得该进程放弃处理机,那么系统那可以把处理机分配给另一个优先权最高的进程。这种方法主要作用与批处理系统中,也可以用于某些对实时性要求不严格的实时系统中;
      • 抢占式优先权算法:系统同样是把处理机优先分配给优先权最高的进程。但是在执行期间,如果出现了一个优先权更高的进程,那么调度程序就会停止当前进程的执行,重新把处理机分配给新到的优先权更高的进程。这种算法能够更好地满足紧迫作业的需求,所以常用于要求比较严格的实时系统中,以及对性能要求较高的批处理和分时系统中。
    • 高响应比优先调度算法:在批处理系统中,短作业优先算法是一种比较好的算法,但是其不足之处是长作业可能会长时间轮不到运行。如果我们能为每个作业引入前面所述的动态优先权,并使作业的优先级随着等待时间的增加而以速率a提高,则长作业在等待一定的时间后,必然有机会分配到处理机。该优先权的变化规律可描述为: $R_{p} = \frac{等待时间 + 要求服务时间}{要求服务时间} = \frac{响应时间}{要求服务时间}$ 缺点是在每次进行调度之前,都需要先进行响应比的计算,这会增加系统的开销。
    • 时间片轮转算法:系统将所有的就绪进程按先来先服务的原则排成队列,每次调度时,都把 CPU 分配给队首的进程,并令其执行一个时间片。时间片的大小从几 ms 到几百 ms。当执行的时间片用完时,就由一个计时器发出时钟中断请求,调度程序便根据此信号来停止该进程的执行,并将该进程送往就绪队列的队尾。接着把 CPU 分配给就绪队列中的新的队首的进程,同时让它也执行一个时间片。这样就能保证就绪队列中的每个进程都可以在给定的时间内均能获得一定的执行时间(循环操作)。
    • 多级反馈队列调度算法:多级反馈队列算法是目前公认的一种较好的进程调度算法,它不必事先知道各种进程所需的执行时间,而且可以满足各种类型进程的需要。该调度算法的实施过程如下所示:
      1. 事先设置多个就绪队列,并为每个队列赋予不同的优先级。比如,第一个队列的优先级最高,第二个队列次之,其余各个队列的优先级逐个降低。该算法是赋予各个队列中进程执行时间片的大小也是不相同的。**优先级越高的队列,每个进程的执行时间片就愈小。**这意味着,优先级低的进程,一个时间片的执行时间就越长。
      2. 当一个新进程进入内存后,首先放在第一队列的末尾,按照先进先出的原则排队等待进行调度。当轮到该进程执行的时候,如果能够在第一队列设置的时间片内完成,就完成后撤离系统;如果尚未完成,则转而进入第二队列的队尾,同样按照先进先出的原则进行等待;以此类推。即「当一个长作业(进程)从第一队列依次降到第 n 队列进行执行后,在第 n 队列便会采取按照时间轮转的方式进行」。
      3. 同时,仅有当第一队列是空闲的时候,调度程序才会调度第二队列的进程去运行。即「仅当第 1~(i-1)队列均为空时,才会调用第i队列中的进程运行」。假如处理机正在第 i 队列中为某进程服务时,又有新的进程进入到了优先权更高的队列中,则新的进程会抢占现在正在执行进程的处理机,即调度程序会把正在运行的进程放回到第 i 队列的队尾,把处理机分配给优先权更高的进程。
  11. 进程同步有哪几种机制

    • 信号量机制(Semaphore):一个信号量只能置一次初值,以后只能对之进行 p 操作或 v 操作。
      • 优点:PV 操作能够实现对临界区的管理要求;实现简单;允许使用它的代码休眠,持有锁的时间可相对较长
      • 缺点:信号量机制必须有公共内存,不能用于分布式操作系统,这是它最大的缺点。
    • 自旋锁:自旋锁是为了保护共享资源提出的一种锁机制。调用者申请的资源如果被占用,即自旋锁被已经被别的执行单元保持,则调用者一直循环在那里看是否该自旋锁的保持着已经释放了锁。
      • 缺点:自旋锁是一种比较低级的保护数据结构和代码片段的原始方式,可能会引起死锁或者过多地占用CPU资源
    • 管程:是一种集中式同步进程。其基本思想是将共享变量和对它们的操作集中在一个模块中,操作系统或并发程序就由这样的模块构成。这样模块之间联系清晰,便于维护和修改,易于保证正确性。
    • 会合:进程直接进行相互作用。
    • 分布式系统:由于在分布式操作系统中没有公共内存,因此参数全为值参,而且不可为指针。
  12. 死锁的处理基本策略和常用方法

    解决死锁的常用策略:鸵鸟策略预防策略避免策略检测与解除策略

    解决死锁的基本方法:预防死锁避免死锁检测死锁解除死锁

    1. 预防死锁

      • 破坏互斥条件:允许进程同时访问某些资源,即资源一次性分配。(因为有的资源是不允许被同时访问的,所以这种方法有时没有实用价值)。

      • 破坏请求和等待的条件:实行资源的预先分配策略。即进程在运行前一次性地向系统申请它所需要的全部资源。如果某个进程所需的全部资源得不到满足,则不分配任何资源,这个进程就暂不运行。只有当系统能够满足当前进程的全部资源需求时,才一次性地将它所申请的资源都全部分配给该进程。这样这个运行的进程已占有了它所需的全部资源,又不会再去申请资源,所以就不会发生死锁现象。

        缺点

        • 在许多情况下,一个进程在执行之前不可能知道它所需要的全部资源是多少。因为进程在执行是动态的,是不可预测的;
        • 资源利用率低。因为无论这个进程申请的资源何时才要用到,一个进程也只有在占有所需的全部资源后才能执行。也就是说,即使有些资源仅仅被该进程只使用一次,也只能等到该进程结束后才能释放,这就会出现长期占有资源却不利用的情况,是一种资源的极大浪费;
        • 降低了进程的并发性。因为资源是有限的,再加上又存在浪费,所以这样能分配到全部资源的进程个数就少了。
      • 破坏了不可剥夺的条件:即允许进程强行从占有者那里夺取资源。即当一个进程已占有了某些资源,它申请新的资源失败了或者不能立即满足的话,就必须释放锁占有的全部资源。它将所释放的资源分配给其它进程,就相当于它的进程占有的资源被其它资源强行占有了(实现很困难,会降低系统性能)。

      • 破坏环路等待条件:采用资源有序分配的方法。即把资源事先分类编号,按编号分配,使得资源在使用时不会形成环路。要求所有进程对资源的请求必须严格按照资源序号递增的顺序申请。

        缺点

        • 限制了进程对资源的请求,同时给系统中所有资源合理地编号也很困难,会增加系统的开销;
        • 为了遵循按照编号去申请的次序,暂不使用的资源也需要提前申请,这样会增加进程对资源的占用时间;
    2. 避免死锁

      因为预防死锁的几个策略都会严重的损害系统性能,所以在避免死锁这块要添加较弱的限制,从而获得较满意的系统性能。

      由于在避免死锁的策略中,可以允许进程动态地申请资源。所以,系统在进行资源分配之前可以预先计算好资源分配的安全性。若此次分配不会导致系统进入不安全状态,则将资源分配给进程;否则,就让进程等待。最有代表性的避免死锁算法是银行家算法

      银行家算法由 Dijstra 首先提出并解决的。即「当一个进程申请使用资源的时候,银行家算法通过先试探分配给该进程资源,然后通过安全性算法判断分配后的系统是否处于安全状态,若不安全则试探的分配就作废,让进程继续等待」。

      银行家算法

    3. 检测死锁

      首先为每个进程和每个资源指定一个唯一的号码;然后建立资源分配表进程等代表。例如:

    4. 解除死锁

      当发现有进程死锁后,就可以立即把它从死锁状态中解脱出来。常用的方法有:

      • 剥夺资源:从其他进程剥夺足够数量的资源给死锁进程,就可以接触死锁状态
      • 撤销进程:可以直接撤销死锁进程或者撤销代价最下的进程,直至有足够的资源可用让死锁状态消除为止;(代价是指优先级、运行代价、进程的重要性和价值等)
  13. 线程里面 sleep 和 wait 什么区别

    最直白地说,sleep 是让线程休眠,休眠一段时间后会继续执行;wait 是等待,需要唤醒后再继续执行。然后从以下几个方面来说,又有具体的不同:

    • 使用方面:sleep 方法是线程类 Thread 的方法,而 wait 是 Object 顶级类的方法;
    • CPU及锁资源释放:sleep 不会释放当前持有对象的锁资源,到时间后会继续执行;wait 会释放所有的锁并且需要notify/notifyAll后,重新获取到对象资源后才能继续执行;
    • 处理异常方面:sleep 需要捕获或者抛出异常,而 wait/notify/notifyAll 则不需要;
    • synchronized:sleep 方法不依赖于同步锁synchronized,但是 wait 需要依赖于synchronized
    • 唤醒:sleep 不需要被唤醒(休眠之后自动退出阻塞状态),但是 wait 需要(需要被别人中断)
  14. 说一下操作系统里的缓存

    操作系统的缓存是「为了提高系统的存取速度,在地址映射机制中增加一个小容量的寄存器,即快表,用来存放当前访问最频繁的少数活动页面的页号,当用户需要时就可以通过快表查询,大大提高了查询速度」。