操做系统面试常见题型

进程 vs 线程

  • 进程(process)与线程(thread)最大的区别是进程拥有本身的地址空间,某进程内的线程对于其余进程不可见,即进程A不能经过传地址的方式直接读写进程B的存储区域。进程之间的通讯须要经过进程间通讯(Inter-process communication,IPC)。与之相对的,同一进程的各线程间之间能够直接经过传递地址或全局变量的方式传递信息linux

  • 进程做为操做系统中拥有资源和独立调度的基本单位,能够拥有多个线程。一般操做系统中运行的一个程序就对应一个进程。在同一进程中,线程的切换不会引发进程切换。在不一样进程中进行线程切换,如从一个进程内的线程切换到另外一个进程中的线程时,会引发进程切换。相比进程切换,线程切换的开销要小不少。线程于进程相互结合可以提升系统的运行效率。程序员

线程能够分为两类:算法

  • 用户级线程(user level thread):对于这类线程,有关线程管理的全部工做都由应用程序完成,内核意识不到线程的存在。在应用程序启动后,操做系统分配给该程序一个进程号,以及其对应的内存空间等资源。应用程序一般先在一个线程中运行,该线程被成为主线程。在其运行的某个时刻,能够经过调用线程库中的函数建立一个在相同进程中运行的新线程。用户级线程的好处是很是高效,不须要进入内核空间,但并发效率不高。数组

  • 内核级线程(kernel level thread):对于这类线程,有关线程管理的全部工做由内核完成,应用程序没有进行线程管理的代码,只能调用内核线程的接口。内核维护进程及其内部的每一个线程,调度也由内核基于线程架构完成。内核级线程的好处是,内核能够将不一样线程更好地分配到不一样的CPU,以实现真正的并行计算。缓存

事实上,在现代操做系统中,每每使用组合方式实现多线程,即线程建立彻底在用户空间中完成,而且一个应用程序中的多个用户级线程被映射到一些内核级线程上,至关因而一种折中方案。安全

上下文切换

  • 对于单核单线程CPU而言,在某一时刻只能执行一条CPU指令。上下文切换(Context Switch)是一种将CPU资源从一个进程分配给另外一个进程的机制。从用户角度看,计算机可以并行运行多个进程,这偏偏是操做系统经过快速上下文切换形成的结果。在切换的过程当中,操做系统须要先存储当前进程的状态(包括内存空间的指针,当前执行完的指令等等),再读入下一个进程的状态,而后执行此进程。服务器

系统调用与库函数的区别

  • 系统调用(System call)是程序向系统内核请求服务的方式。能够包括硬件相关的服务(例如,访问硬盘等),或者建立新进程,调度其余进程等。系统调用是程序和操做系统之间的重要接口。网络

  • 库函数:把一些经常使用的函数编写完放到一个文件里,编写应用程序时调用,这是由第三方提供的,发生在用户地址空间多线程

  • 移植性方面,不一样操做系统的系统调用通常是不一样的,移植性差;而在全部的ANSI C编译器版本中,C库函数是相同的。架构

  • 调用开销方面,系统调用须要在用户空间和内核环境间切换,开销较大;而库函数调用属于“过程调用”,开销较小。

守护、僵尸、孤儿进程的概念

  • 守护进程:运行在后台的一种特殊进程,独立于控制终端并周期性地执行某些任务

  • 僵尸进程:一个进程 fork 子进程,子进程退出,而父进程没有wait/waitpid子进程,那么子进程的进程描述符仍保存在系统中,这样的进程称为僵尸进程。

  • 孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,这些子进程称为孤儿进程。(孤儿进程将由 init 进程收养并对它们完成状态收集工做)

分时系统与实时系统的区别

  • 分时系统(Sharing time system):系统把CPU时间分红很短的时间片,轮流地分配给多个做业。优势:对多个用户的多个做业都能保证足够快的响应时间,而且有效提升了资源的利用率。

  • 实时系统(Real-time system):系统对外部输入的信息,可以在规定的时间内(截止期限)处理完毕并作出反应。优势:可以集中地及时地处理并做出反应,高可靠性,安全性。

  • 一般计算机采用的是sharing time,即多个进程/用户之间共享CPU,从形势上实现多任务。各个用户/进程之间的调度并不是精准度特别高,若是一个进程被锁住,能够给它分配更多的时间。而实时操做系统则不一样,软件和硬件必须听从严格的deadline,超过期限的进程可能直接被终止。在这样的操做系统中,每次加锁都须要仔细考虑。

Semaphore(信号量) Vs Mutex(互斥锁)

  • 当用户创立多个线程/进程时,若是不一样线程/进程同时读写相同的内容,则可能形成读写错误,或者数据不一致。此时,须要经过加锁的方式,控制临界区(critical section)的访问权限。对于semaphore而言,在初始化变量的时候能够控制容许多少个线程/进程同时访问一个临界区,其余的线程/进程会被堵塞,直到有人解锁。

  • Mutex至关于只容许一个线程/进程访问的semaphore。此外,根据实际须要,人们还实现了一种读写锁(read-write lock),它容许同时存在多个阅读者(reader),但任什么时候候至多只有一个写者(writer),且不能于读者共存。

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

  • 所谓的逻辑地址,是指计算机用户(例如程序开发者),看到的地址。例如,当建立一个长度为100的整型数组时,操做系统返回一个逻辑上的连续空间:指针指向数组第一个元素的内存地址。因为整型元素的大小为4个字节,故第二个元素的地址时起始地址加4,以此类推。事实上,逻辑地址并不必定是元素存储的真实地址,即数组元素的物理地址(在内存条中所处的位置),并不是是连续的,只是操做系统经过地址映射,将逻辑地址映射成连续的,这样更符合人们的直观思惟

  • 另外一个重要概念是虚拟内存。操做系统读写内存的速度能够比读写磁盘的速度快几个量级。可是,内存价格也相对较高,不能大规模扩展。因而,操做系统能够经过将部分不太经常使用的数据移出内存,“存放到价格相对较低的磁盘缓存,以实现内存扩展。操做系统还能够经过算法预测哪部分存储到磁盘缓存的数据须要进行读写,提早把这部分数据读回内存。虚拟内存空间相对磁盘而言要小不少,所以,即便搜索虚拟内存空间也比直接搜索磁盘要快。惟一慢于磁盘的多是,内存、虚拟内存中都没有所须要的数据,最终还须要从硬盘中直接读取。这就是为何内存和虚拟内存中须要存储会被重复读写的数据,不然就失去了缓存的意义。现代计算机中有一个专门的转译缓冲区(Translation Lookaside Buffer,TLB),用来实现虚拟地址到物理地址的快速转换。

与内存/虚拟内存相关的还有以下两个概念:
1) Resident Set

  • 当一个进程在运行的时候,操做系统不会一次性加载进程的全部数据到内存,只会加载一部分正在用,以及预期要用的数据。其余数据可能存储在虚拟内存,交换区和硬盘文件系统上。被加载到内存的部分就是resident set。

2) Thrashing

  • 因为resident set包含预期要用的数据,理想状况下,进程运行过程当中用到的数据都会逐步加载进resident set。但事实每每并不是如此:每当须要的内存页面(page)不在resident set中时,操做系统必须从虚拟内存或硬盘中读数据,这个过程被称为内存页面错误(page faults)。当操做系统须要花费大量时间去处理页面错误的状况就是thrashing。

文件系统

  • Unix风格的文件系统利用树形结构管理文件。每一个节点有多个指针,指向下一层节点或者文件的磁盘存储位置。文件节点还附有文件的操做信息(metadata),包括修改时间,访问权限等等。

  • 用户的访问权限经过能力表(Capability List)和访问控制表(Access Control List)实现。前者从文件角度出发,标注了每一个用户能够对该文件进行何种操做。后者从用户角度出发,标注了某用户能够以什么权限操做哪些文件。

  • Unix的文件权限分为读、写和执行,用户组分为文件拥有者,组和全部用户。能够经过命令对三组用户分别设置权限。

请问死锁的条件是什么?以及如何处理死锁问题?

  • 互斥条件(Mutual exclusion):资源不能被共享,只能由一个进程使用。

  • 请求与保持条件(Hold and wait):已经获得资源的进程能够再次申请新的资源。

  • 非抢占条件(No pre-emption):已经分配的资源不能从相应的进程中被强制地剥夺。

  • 循环等待条件(Circular wait):系统中若干进程组成环路,该环路中每一个进程都在等待相邻进程正占用的资源。

如何处理死锁问题:

  • 忽略该问题。例如鸵鸟算法,该算法能够应用在极少发生死锁的的状况下。为何叫鸵鸟算法呢,由于传说中鸵鸟看到危险就把头埋在地底下,可能鸵鸟以为看不到危险也就没危险了吧。跟掩耳盗铃有点像。

  • 检测死锁而且恢复。

  • 仔细地对资源进行动态分配,以避免死锁

  • 经过破除死锁四个必要条件之一,来防止死锁产生。

动态连接库与静态连接库的区别

静态库

  • 静态库是一个外部函数与变量的集合体。静态库的文件内容,一般包含一堆程序员自定的变量与函数,其内容不像动态连接库那么复杂,在编译期间由编译器与链接器将它集成至应用程序内,并制做成目标文件以及能够独立运做的可执行文件。而这个可执行文件与编译可执行文件的程序,都是一种程序的静态建立(static build)。

clipboard.png

动态库

  • 静态库很方便,可是若是咱们只是想用库中的某一个函数,却仍然得把全部的内容都连接进去。一个更现代的方法则是使用共享库,避免了在文件中静态库的大量重复。

  • 动态连接能够在首次载入的时候执行(load-time linking),这是 Linux 的标准作法,会由动态连接器ld-linux.so 完成,比方标准 C 库(libc.so) 一般就是动态连接的,这样全部的程序能够共享同一个库,而不用分别进行封装。

  • 动态连接也能够在程序开始执行的时候完成(run-time linking),在 Linux 中使用 dlopen()接口来完成(会使用函数指针),一般用于分布式软件,高性能服务器上。并且共享库也能够在多个进程间共享。

  • 连接使得咱们能够用多个对象文件构造咱们的程序。能够在程序的不一样阶段进行(编译、载入、运行期间都可),理解连接能够帮助咱们避免遇到奇怪的错误

clipboard.png

进程间通讯

  • 管道管道是单向的、先进先出的、无结构的、固定大小的字节流,它把一个进程的标准输出和另外一个进程的标准输入链接在一块儿。写进程在管道的尾端写入数据,读进程在管道的道端读出数据。数据读出后将从管道中移走,其它读进程都不能再读到这些数据。管道提供了简单的流控制机制。进程试图读空管道时,在有数据写入管道前,进程将一直阻塞。一样地,管道已经满时,进程再试图写管道,在其它进程从管道中移走数据以前,写进程将一直阻塞。

  • 信号量信号量是一个计数器,能够用来控制多个进程对共享资源的访问。它常做为一种锁机制,防止某进程正在访问共享资源时,其它进程也访问该资源。所以,主要做为进程间以及同一进程内不一样线程之间的同步手段。

  • 消息队列消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点

  • 信号:信号是一种比较复杂的通讯方式,用于通知接收进程某个事件已经发生。

  • 共享内存共享内存就是映射一段能被其它进程所访问的内存,这段共享内存由一个进程建立,但多个进程均可以访问。共享内存是最快的IPC方式,它是针对其它进程间通讯方式运行效率低而专门设计的。它每每与其它通讯机制(如信号量)配合使用,来实现进程间的同步和通讯。

  • 套接字:套接字也是一种进程间通讯机制,与其它通讯机制不一样的是,它可用于不一样机器间的进程通讯。

中断与系统调用

所谓的中断就是在计算机执行程序的过程当中,因为出现了某些特殊事情,使得CPU暂停对程序的执行,转而去执行处理这一事件的程序。等这些特殊事情处理完以后再回去执行以前的程序。中断通常分为三类:

  • 由计算机硬件异常或故障引发的中断,称为内部异常中断

  • 由程序中执行了引发中断的指令而形成的中断,称为软中断(这也是和咱们将要说明的系统调用相关的中断);

  • 由外部设备请求引发的中断,称为外部中断。简单来讲,对中断的理解就是对一些特殊事情的处理。

与中断紧密相连的一个概念就是中断处理程序了。当中断发生的时候,系统须要去对中断进行处理,对这些中断的处理是由操做系统内核中的特定函数进行的,这些处理中断的特定的函数就是咱们所说的中断处理程序了。

另外一个与中断紧密相连的概念就是中断的优先级。中断的优先级说明的是当一个中断正在被处理的时候,处理器能接受的中断的级别。中断的优先级也代表了中断须要被处理的紧急程度。每一个中断都有一个对应的优先级,当处理器在处理某一中断的时候,只有比这个中断优先级高的中断能够被处理器接受而且被处理。优先级比这个当前正在被处理的中断优先级要低的中断将会被忽略。

典型的中断优先级以下所示:

  • 机器错误 > 时钟 > 磁盘 > 网络设备 > 终端 > 软件中断


在讲系统调用以前,先说下进程的执行在系统上的两个级别:用户级和核心级,也称为用户态和系统态(user mode and kernel mode)

  • 程序的执行通常是在用户态下执行的,但当程序须要使用操做系统提供的服务时,好比说打开某一设备、建立文件、读写文件等,就须要向操做系统发出调用服务的请求,这就是系统调用。

  • Linux系统有专门的函数库来提供这些请求操做系统服务的入口,这个函数库中包含了操做系统所提供的对外服务的接口。当进程发出系统调用以后,它所处的运行状态就会由用户态变成核心态。但这个时候,进程自己其实并无作什么事情,这个时候是由内核在作相应的操做,去完成进程所提出的这些请求

  • 系统调用和中断的关系就在于,当进程发出系统调用申请的时候,会产生一个软件中断。产生这个软件中断之后,系统会去对这个软中断进行处理,这个时候进程就处于核心态了

用户态和核心态之间的区别是什么呢?

  • 用户态的进程能存取它们本身的指令和数据,但不能存取内核指令和数据(或其余进程的指令和数据)

  • 核心态下的进程可以存取内核和用户地址某些机器指令是特权指令,在用户态下执行特权指令会引发错误。在系统中内核并非做为一个与用户进程平行的估计的进程的集合。

进程的三种状态

  • 阻塞态:等待某个事件的完成;

  • 就绪态:等待系统分配处理器以便运行;

  • 运行态:占有处理器正在运行。

clipboard.png

运行态→阻塞态:每每是因为等待外设,等待主存等资源分配或等待人工干预而引发的。
阻塞态→就绪态:则是等待的条件已知足,只需分配处处理器后就能运行。
运行态→就绪态:不是因为自身缘由,而是由外界缘由使运行状态的进程让出处理器,这时候就变成就绪态。例如时间片用完,或有更高优先级的进程来抢占处理器等。
就绪态→运行态:系统按某种策略选中就绪队列中的一个进程占用处理器,此时就变成了运行态

进程调度

调度种类

  • 高级调度:(High-Level Scheduling)又称为做业调度,它决定把后备做业调入内存运行;

  • 低级调度:(Low-Level Scheduling)又称为进程调度,它决定把就绪队列的某进程得到CPU;

  • 中级调度:(Intermediate-Level Scheduling)又称为在虚拟存储器中引入,在内、外存对换区进行进程对换。

非抢占式调度与抢占式调度

  • 非抢占式:分派程序一旦把处理机分配给某进程后便让它一直运行下去,直到进程完成或发生进程调度进程调度某事件而阻塞时,才把处理机分配给另外一个进程。

  • 抢占式:操做系统将正在运行的进程强行暂停,由调度程序将CPU分配给其余就绪进程的调度方式。

调度策略的设计

  • 响应时间: 从用户输入到产生反应的时间

  • 周转时间: 从任务开始到任务结束的时间

CPU任务能够分为交互式任务批处理任务,调度最终的目标是合理的使用CPU,使得交互式任务的响应时间尽量短,用户不至于感到延迟,同时使得批处理任务的周转时间尽量短,减小用户等待的时间。

调度算法

FIFO或First Come, First Served (FCFS)

  • 调度的顺序就是任务到达就绪队列的顺序。

  • 公平、简单(FIFO队列)、非抢占、不适合交互式。

  • 未考虑任务特性,平均等待时间能够缩短。

Shortest Job First (SJF)

  • 最短的做业(CPU区间长度最小)最早调度。

  • SJF能够保证最小的平均等待时间。

Shortest Remaining Job First (SRJF)

  • SJF的可抢占版本,比SJF更有优点。

  • SJF(SRJF): 如何知道下一CPU区间大小?根据历史进行预测: 指数平均法。

优先权调度

  • 每一个任务关联一个优先权,调度优先权最高的任务。

  • 注意:优先权过低的任务一直就绪,得不到运行,出现“饥饿”现象。

Round-Robin(RR)

  • 设置一个时间片,按时间片来轮转调度(“轮叫”算法)

  • 优势: 定时有响应,等待时间较短;缺点: 上下文切换次数较多;

  • 时间片太大,响应时间太长;吞吐量变小,周转时间变长;当时间片过长时,退化为FCFS。

多级队列调度

  • 按照必定的规则创建多个进程队列

  • 不一样的队列有固定的优先级(高优先级有抢占权)

  • 不一样的队列能够给不一样的时间片和采用不一样的调度方法

  • 存在问题1:无法区分I/O bound和CPU bound;

  • 存在问题2:也存在必定程度的“饥饿”现象;

多级反馈队列

  • 在多级队列的基础上,任务能够在队列之间移动,更细致的区分任务。

  • 能够根据“享用”CPU时间多少来移动队列,阻止“饥饿”。

  • 最通用的调度算法,多数OS都使用该方法或其变形,如UNIX、Windows等。

多级反馈队列调度算法描述:

clipboard.png

  • 进程在进入待调度的队列等待时,首先进入优先级最高的Q1等待。

  • 首先调度优先级高的队列中的进程。若高优先级中队列中已没有调度的进程,则调度次优先级队列中的进程。例如:Q1,Q2,Q3三个队列,只有在Q1中没有进程等待时才去调度Q2,同理,只有Q1,Q2都为空时才会去调度Q3。

  • 对于同一个队列中的各个进程,按照时间片轮转法调度。好比Q1队列的时间片为N,那么Q1中的做业在经历了N个时间片后若尚未完成,则进入Q2队列等待,若Q2的时间片用完后做业还不能完成,一直进入下一级队列,直至完成。

  • 在低优先级的队列中的进程在运行时,又有新到达的做业,那么在运行完这个时间片后,CPU立刻分配给新到达的做业(抢占式)。

一个简单的例子
假设系统中有3个反馈队列Q1,Q2,Q3,时间片分别为2,4,8。如今有3个做业J1,J2,J3分别在时间 0 ,1,3时刻到达。而它们所须要的CPU时间分别是3,2,1个时间片。

  • 时刻0 J1到达。 因而进入到队列1 ,运行1个时间片 ,时间片还未到,此时J2到达。

  • 时刻1 J2到达。 因为时间片仍然由J1掌控,因而等待。J1在运行了1个时间片后,已经完成了在Q1中的2个时间片的限制,因而J1置于Q2等待被调度。如今处理机分配给J2。

  • 时刻2 J1进入Q2等待调度,J2得到CPU开始运行。

  • 时刻3 J3到达,因为J2的时间片未到,故J3在Q1等待调度,J1也在Q2等待调度。

  • 时刻4 J2处理完成,因为J3,J1都在等待调度,可是J3所在的队列比J1所在的队列的优先级要高,因而J3被调度,J1继续在Q2等待。

  • 时刻5 J3通过1个时间片,完成。

  • 时刻6 因为Q1已经空闲,因而开始调度Q2中的做业,则J1获得处理器开始运行。 J1再通过一个时间片,完成了任务。因而整个调度过程结束。

临界资源与临界区

  • 在操做系统中,进程是占有资源的最小单位(线程能够访问其所在进程内的全部资源,但线程自己并不占有资源或仅仅占有一点必须资源)。但对于某些资源来讲,其在同一时间只能被一个进程所占用。这些一次只能被一个进程所占用的资源就是所谓的临界资源。典型的临界资源好比物理上的打印机,或是存在硬盘或内存中被多个进程所共享的一些变量和数据等(若是这类资源不被当作临界资源加以保护,那么颇有可能形成丢数据的问题)。

  • 对于临界资源的访问,必须是互斥进行。也就是当临界资源被占用时,另外一个申请临界资源的进程会被阻塞,直到其所申请的临界资源被释放。而进程内访问临界资源的代码被成为临界区。

信号量

信号量是一个肯定的二元组(s,q),其中s是一个具备非负初值的整形变量,q是一个初始状态为空的队列,整形变量s表示系统中某类资源的数目:

  • 当其值 ≥ 0 时,表示系统中当前可用资源的数目

  • 当其值 < 0 时,其绝对值表示系统中因请求该类资源而被阻塞的进程数目

除信号量的初值外,信号量的值仅能由P操做和V操做更改,操做系统利用它的状态对进程和资源进行管理。

P操做

P 操做记为P(s),其中s为一信号量,它执行时主要完成如下动做:

  • s.value = s.value - 1; /可理解为占用1个资源,若原来就没有则记账“欠”1个/

若s.value ≥ 0,进程继续执行,不然(即s.value < 0)进程被阻塞,并将该进程插入到信号量s的等待队列s.queue中。

  • P操做能够理解为分配资源的计数器,或是使进程处于等待状态的控制指令

V操做

V 操做记为V(s),其中s为一信号量,它执行时,主要完成如下动做:

  • s.value = s.value + 1;/可理解为归还1个资源,若原来就没有则意义是用此资源还1个欠账/

若s.value > 0,进程继续执行,不然从信号量s的等待队s.queue中移出第一个进程,使其变为就绪状态,而后返回原进程继续执行。

V操做能够理解为归还资源的计数器,或是唤醒进程使其处于就绪状态的控制指令

IO多路复用

IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用以下场合:

  • 当客户处理多个描述字时(通常是交互式输入和网络套接口),必须使用I/O复用。

  • 当一个客户同时处理多个套接口时,而这种状况是可能的,但不多出现。

  • 若是一个TCP服务器既要处理监听套接口,又要处理已链接套接口,通常也要用到I/O复用。

  • 若是一个服务器即要处理TCP,又要处理UDP,通常要使用I/O复用。

  • 若是一个服务器要处理多个服务或多个协议,通常要使用I/O复用。

  • 与多进程和多线程技术相比,I/O多路复用技术的最大优点是系统开销小,系统没必要建立进程/线程,也没必要维护这些进程/线程,从而大大减少了系统的开销。

相关文章
相关标签/搜索