本文档为面试精华版,如果是初学者,建议从专栏学习:操作系统专栏

1. 聊聊进程和线程

进程

  • 进程是进程实体的运行过程,是系统进行资源分配和调度的一一个独立单位。
  • 进程实体由程序段、相关数据段和PCB组成

线程

  • 线程的提出源于在并发的时候,进程的切换需要消耗很大的时空开销,而线程的提出可以提高并发时系统的性能

进程和线程的区别?

  1. 拥有资源:进程是资源分配的基本单位,但是线程不拥有资源,线程可以访问隶属进程的资源
  2. 调度:线程是独立调度的基本单位,在同一进程中,线程的切换不会引起进程切换,从一个进程中的线程切换到另一个进程中的线程时,会引起进程切换
  3. 系统开销:由于创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、I/O 设备等,所付出的开销远大于创建或撤销线程时的开销。类似地,在进行进程切换时,涉及当前执行进程 CPU 环境的保存及新调度进程 CPU 环境的设置,而线程切换时只需保存和设置少量寄存器内容,开销很小
  4. 通信方面:线程间可以通过直接读写同一进程中的数据进行通信,但是进程通信需要借助 IPC。

2. 进程有几种状态?

有五种状态

  • 创建:进程正在被分配资源
  • 结束:当进程正常或者异常退出。
  • 就绪:进程已处于准备运⾏状态,即进程获得了除了处理器之外的⼀切所需资源,⼀旦得到处理器资源(处理器分配的时间⽚)即可运⾏
  • 执行:进程正在处理器上上运⾏
  • 阻塞:进程正在等待某⼀事件⽽暂停运⾏如等待某资源为可⽤或等待 IO 操作完成。即使处理器空闲,该进程也不能运⾏

应该注意以下内容

  • 只有就绪态和运行态可以相互转换,其它的都是单向转换。就绪状态的进程通过调度算法从而获得 CPU 时间,转为运行状态;而运行状态的进程,在分配给它的 CPU 时间片用完之后就会转为就绪状态,等待下一次调度。
  • 阻塞状态是缺少需要的资源从而由运行状态转换而来,但是该资源不包括 CPU 时间,缺少 CPU 时间会从运行态转换为就绪态。

3. 进程间的通信方式

大概有 7 种常见的进程间的通信⽅式

  1. 管道/匿名管道(Pipes)⽤于具有亲缘关系的⽗⼦进程间或者兄弟进程之间的通信。
  2. 有名管道(Names Pipes) :克服了管道只能⽤于亲缘关系的进程间通信这个缺点,严格遵循先进先出(first in first out)。有名管道以磁盘⽂件的⽅式存在,可以实现本机任意两个进程通信。
  3. 信号(Signal) :信号是⼀种⽐较复杂的通信⽅式,⽤于通知接收进程某个事件已经发⽣;
  4. 消息队列(Message Queuing) :消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识。管道和消息队列的通信数据都是先进先出的原则。与管道(⽆名管道:只存在于内存中的⽂件;命名管道:存在于实际的磁盘介质或者⽂件系统)不同的是消息队列存放在内核中,只有在内核重启(即,操作系统重启)或者显示地删除⼀个消息队列时,该消息队列才会被真正的删除。消息队列可以实现消息的随机查询,消息不⼀定要以先进先出的次序读取,也可以按消息的类型读取.⽐ FIFO 更有优势。 消息队列克服了信号承载信息量少,管道只能承载⽆格式字节流以及缓冲区大小受限等缺。
  5. 信号量(Semaphores)信号量是⼀个计数器,⽤于多进程对共享数据的访问,信号量的意图在 于进程间同步。这种通信⽅式主要⽤于解决与同步相关的问题并避免竞争条件。
  6. 共享内存(Shared memory)使得多个进程可以访问同⼀块内存空间,不同进程可以及时看到对 ⽅进程中对共享内存中数据的更新。这种⽅式需要依靠某种同步操作,如互斥锁和信号量等。可 以说这是最有⽤的进程间通信⽅式。
  7. 套接字(Sockets) : 此⽅法主要⽤于在客户端和服务器之间通过⽹络进⾏通信。套接字是⽀持
    TCP/IP 的⽹络通信的基本操作单元,可以看做是不同主机之间的进程进⾏双向通信的端点,简
    单的说就是通信的两⽅的⼀种约定,⽤套接字中的相关函数来完成通信过程。

4. 线程间的同步的⽅式有哪些呢?

线程同步是两个或多个共享关键资源的线程的并发执行。应该同步线程以避免关键的资源使用冲突。操作系统-般有下面三种线程同步的方式:

  1. 互斥量(Mutex): 采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为.互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。比如Java 中的synchronized关键词和各种Lock 都是这种机制。
  2. 信号量(Semphares) :它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量
  3. 事件(Event) : 通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作

5. 你知道操作系统中进程的调度算法有哪些吗?

不同环境的调度算法目标不同,因此需要针对不同环境来讨论调度算法。

a. 批处理系统

批处理系统没有太多的用户操作,在该系统中,调度算法目标是保证吞吐量和周转时间(从提交到终止的时间)。

  1. 先来先服务 first-come first-serverd(FCFS)
    非抢占式的调度算法,按照请求的顺序进行调度。
    有利于长作业,但不利于短作业,因为短作业必须一直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成了短作业等待时间过长。
  2. 短作业优先 shortest job first(SJF)
    非抢占式的调度算法,按估计运行时间最短的顺序进行调度。
    长作业有可能会饿死,处于一直等待短作业执行完毕的状态。因为如果一直有短作业到来,那么长作业永远得不到调
    度。
  3. 最短剩余时间优先 shortest remaining time next(SRTN)
    最短作业优先的抢占式版本,按剩余运行时间的顺序进行调度。 当一个新的作业到达时,其整个运行时间与当前进程的剩余时间作比较。如果新的进程需要的时间更少,则挂起当前进程,运行新的进程。否则新的进程等待。

b. 交互式系统

交互式系统有大量的用户交互操作,在该系统中调度算法的目标是快速地进行响应。

  1. 时间片轮转
    将所有就绪进程按 FCFS 的原则排成一个队列,每次调度时,把 CPU 时间分配给队首进程,该进程可以执行一个时间片。当时间片用完时,由计时器发出时钟中断,调度程序便停止该进程的执行,并将它送往就绪队列的末尾,同时继续把 CPU 时间分配给队首的进程。
    时间片轮转算法的效率和时间片的大小有很大关系:
    • 因为进程切换都要保存进程的信息并且载入新进程的信息,如果时间片太小,会导致进程切换得太频繁,在进程切换上就会花过多时间。
    • 而如果时间片过长,那么实时性就不能得到保证

  1. 优先级调度
    为每个进程分配一个优先级,按优先级进行调度。
    为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级。
  2. 多级反馈队列
    一个进程需要执行 100 个时间片,如果采用时间片轮转调度算法,那么需要交换 100 次。
    多级队列是为这种需要连续执行多个时间片的进程考虑,它设置了多个队列,每个队列时间片大小都不同,例如1,2,4,8,…。进程在第一个队列没执行完,就会被移到下一个队列。这种方式下,之前的进程只需要交换 7 次。
    每个队列优先权也不同,最上面的优先权最高。因此只有上一个队列没有进程在排队,才能调度当前队列上的进程。
    可以将这种调度算法看成是时间片轮转调度算法和优先级调度算法的结合。

6. 发生死锁的必要条件

  • 互斥:每个资源要么已经分配给了一个进程,要么就是可用的。
  • 占有和等待:已经得到了某个资源的进程可以再请求新的资源。
  • 不可抢占:已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的进程显式地释放。
  • 环路等待:有两个或者两个以上的进程组成一条环路,该环路中的每个进程都在等待下一个进程所占有的资源。

7. 解决死锁的方式

主要有以下四种方法

  • 鸵鸟策略:因为解决死锁问题的代价很高,因此鸵鸟策略这种不采取任务措施的方案会获得更高的性能。
  • 死锁检测与死锁恢复:检测主要是查看当前是否出现了环路等待;恢复可以通过杀死进程或者利用回滚
  • 死锁预防:在程序运行之前破坏发生死锁的条件,预防发生死锁 ,比如说破坏环路等待可以给资源统一编号,进程只能按编号顺序来请求资源。
  • 死锁避免 :使用银行家算法,假设给进程分配资源,看能不能找到一个安全序列,如果系统处于不安全状态,不一定会发生死锁;但是死锁时,系统一定处于不安全状态