操作系统刷题题库
1.进程和线程的区别?
- 调度:进程是资源管理的基本单位,线程是程序执行的基本单位。
- 切换:线程上下文切换比进程上下文切换要快得多。
- 拥有资源: 进程是拥有资源的一个独立单位,线程不拥有系统资源,但是可以访问隶属于进程的资源。
- 系统开销: 创建或撤销进程时,系统都要为之分配或回收系统资源,如内存空间,I/O设备等,OS所付出的开销显著大于在创建或撤销线程时的开销,进程切换的开销也远大于线程切换的开销。
2.协程与线程的区别?
- 线程和进程都是同步机制,而协程是异步机制。
- 线程是抢占式,而协程是非抢占式的。需要用户释放使用权切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力。
- 一个线程可以有多个协程,一个进程也可以有多个协程。
- 协程不被操作系统内核管理,而完全是由程序控制。线程是被分割的CPU资源,协程是组织好的代码流程,线程是协程的资源。但协程不会直接使用线程,协程直接利用的是执行器关联任意线程或线程池。
- 协程能保留上一次调用时的状态。
3.并发和并行有什么区别?
并发就是在一段时间内,多个任务都会被处理;但在某一时刻,只有一个任务在执行。单核处理器可以做到并发。比如有两个进程A和B,A运行一个时间片之后,切换到B,B运行一个时间片之后又切换到A。因为切换速度足够快,所以宏观上表现为在一段时间内能同时运行多个程序。
并行就是在同一时刻,有多个任务在执行。这个需要多核处理器才能完成,在微观上就能同时执行多条指令,不同的程序被放到不同的处理器上运行,这个是物理上的多个进程同时进行。
4.进程与线程的切换流程?
进程切换分两步:
1、切换页表以使用新的地址空间,一旦去切换上下文,处理器中所有已经缓存的内存地址一瞬间都作废了。
2、切换内核栈和硬件上下文。
对于linux来说,线程和进程的最大区别就在于地址空间,对于线程切换,第1步是不需要做的,第2步是进程和线程切换都要做的。
因为每个进程都有自己的虚拟地址空间,而线程是共享所在进程的虚拟地址空间的,因此同一个进程中的线程进行线程切换时不涉及虚拟地址空间的转换。
5.为什么虚拟地址空间切换会比较耗时?
进程都有自己的虚拟地址空间,把虚拟地址转换为物理地址需要查找页表,页表查找是一个很慢的过程,因此通常使用Cache来缓存常用的地址映射,这样可以加速页表查找,这个Cache就是TLB(translation Lookaside Buffer,TLB本质上就是一个Cache,是用来加速页表查找的)。
由于每个进程都有自己的虚拟地址空间,那么显然每个进程都有自己的页表,那么当进程切换后页表也要进行切换,页表切换后TLB就失效了,Cache失效导致命中率降低,那么虚拟地址转换为物理地址就会变慢,表现出来的就是程序运行会变慢,而线程切换则不会导致TLB失效,因为线程无需切换地址空间,因此我们通常说线程切换要比较进程切换块,原因就在这里。
6.进程间通信方式有哪些?
- 管道:管道这种通讯方式有两种限制,一是半双工的通信,数据只能单向流动,二是只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
- 管道可以分为两类:匿名管道和命名管道。匿名管道是单向的,只能在有亲缘关系的进程间通信;命名管道以磁盘文件的方式存在,可以实现本机任意两个进程通信。
- 信号 : 信号是一种比较复杂的通信方式,信号可以在任何时候发给某一进程,而无需知道该进程的状态。
- Linux系统中常用信号: (1)SIGHUP:用户从终端注销,所有已启动进程都将收到该进程。系统缺省状态下对该信号的处理是终止进程。
- (2)SIGINT:程序终止信号。程序运行过程中,按Ctrl+C键将产生该信号。
- (3)SIGQUIT:程序退出信号。程序运行过程中,按Ctrl+\\键将产生该信号。
- (4)SIGBUS和SIGSEGV:进程访问非法地址。
- (5)SIGFPE:运算中出现致命错误,如除零操作、数据溢出等。
- (6)SIGKILL:用户终止进程执行信号。shell下执行kill -9发送该信号。
- (7)SIGTERM:结束进程信号。shell下执行kill 进程pid发送该信号。
- (8)SIGALRM:定时器信号。
- (9)SIGCLD:子进程退出信号。如果其父进程没有忽略该信号也没有处理该信号,则子进程退出后将形成僵尸进程。
- 信号量:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
- 消息队列:消息队列是消息的链接表,包括Posix消息队列和System V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
- 共享内存:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
- Socket:与其他通信机制不同的是,它可用于不同机器间的进程通信。
优缺点:
- 管道:速度慢,容量有限;
- Socket:任何进程间都能通讯,但速度慢;
- 消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题;
- 信号量:不能传递复杂消息,只能用来同步;
- 共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存。
7.进程间同步的方式有哪些?
(1)信号量是一个整型变量,用于表示系统中某种资源的数量。通过p(wait)操作和v(signal)操作来实现进程间的同步。p操作用于申请资源,若资源不足则阻塞当前进程;v操作用于释放资源,若有进程在等待该资源,则唤醒一个进程。在生产者-消费者问题中,设置两个同步信号量empty和full,分别表示当前缓冲区中空位的数量和已放入资源的数量,初始值分别为缓冲区大小n和0。生产者进程在放入产品前执行p(empty)操作,放入产品后执行v(full)操作;消费者进程在取出产品前执行p(full)操作,取出产品后执行v(empty)操作,从而实现生产者和消费者进程的同步。
(2)管程是由一个或多个过程、一个初始化序列和局部数据组成的软件模块。局部数据变量只能被管程的过程访问,任何外部过程都不能访问。一个进程通过调用管程的一个过程进入管程,在任何时候,只能有一个进程在管程中执行,调用管程的任何其他进程都被阻塞,以等待管程可用。管程通过使用条件变量提供对同步的支持。在读者-写者问题中,可以使用管程来管理对共享文件的访问。管程中包含共享文件的状态信息以及对文件进行读写操作的过程。读者进程在读文件前调用管程中的读操作过程,写者进程在写文件前调用管程中的写操作过程,管程根据文件的状态和进程的请求,决定是否允许进程访问文件,从而实现读者和写者进程的同步。
(3)消息传递机制需要操作系统内核的支持,通过消息队列或信箱传递数据,消息以数据块的形式传输。进程之间可以调用由操作系统实现的通信命令来传递消息,实现消息传递即分别实现send和receive两个原语,用于消息的发送和接收。在分布式系统中,不同节点上的进程可以通过消息队列进行通信。一个节点上的进程将数据以消息的形式发送到消息队列中,另一个节点上的进程从消息队列中接收消息并进行处理,从而实现进程间的同步和协作。
(4)屏障是一种同步机制,用于确保多个进程或线程在某个点之前不会继续执行。当进程或线程到达屏障时,它会等待,直到所有进程或线程都到达屏障后才继续执行。
应用示例:在并行计算中,多个进程需要在完成一个阶段的计算后,同步到一个屏障点,然后一起进入下一个阶段的计算。这样可以保证所有进程在同一个阶段开始和结束,避免出现计算结果不一致或进程执行顺序混乱的情况。
(5)条件变量是一种同步机制,用于在多个进程或线程之间传递状态信息。进程或线程在条件不满足时等待,条件满足时被唤醒。通常与互斥锁一起使用,以避免竞争条件。在多线程编程中,一个线程负责生产数据,另一个线程负责消费数据。生产者线程在生产完数据后,通过条件变量通知消费者线程数据已准备好;消费者线程在等待数据时,会阻塞在条件变量上,直到生产者线程通知它数据已准备好,然后消费者线程获取互斥锁,消费数据,消费完成后释放互斥锁,并再次等待条件变量的通知。
8.线程同步的方式有哪些?
1.互斥锁(Mutex):互斥锁是一种简单的同步机制,用于保护共享资源,确保同一时间只有一个线程可以访问。线程在访问资源前需获取锁,访问完后释放锁。若锁已被其他线程获取,请求锁的线程会被阻塞,直至锁被释放。
2.信号量(Semaphore):信号量用于控制同时访问某个资源的线程数量。它维护一个计数器,线程在访问资源前需获取信号量,若计数器大于0,则线程可以继续执行并将计数器减1;若计数器为0,线程则会被阻塞。当线程完成对资源的访问后,会释放信号量,将计数器加1,以允许其他线程获取信号量。
3.条件变量(Condition Variable):条件变量通常与互斥锁一起使用,用于在线程间进行通信和协调。线程可以在条件变量上等待特定条件的满足,当某个线程改变了共享资源的状态,使得条件满足时,它可以通过通知条件变量来唤醒等待的线程。
4.读写锁(Read-Write Lock):读写锁允许多个线程同时读取共享资源,但在写操作时会对资源进行独占访问。读线程可以同时获取读锁来读取资源,而写线程在获取写锁前,需要等待所有读锁和其他写锁被释放。
5.原子操作(Atomic Operations):原子操作是指在执行过程中不会被其他线程干扰的操作,通常由硬件或操作系统提供支持。例如,对一个整数变量的原子加法操作,即使在多个线程同时执行时,也能保证操作的原子性和数据的一致性。
9.线程的分类?
从线程的运行空间来说,分为用户级线程(user-level thread, ULT)和内核级线程(kernel-level, KLT)
内核级线程:这类线程依赖于内核,又称为内核支持的线程或轻量级进程。无论是在用户程序中的线程还是系统进程中的线程,它们的创建、撤销和切换都由内核实现。比如英特尔i5-8250U是4核8线程,这里的线程就是内核级线程
用户级线程:它仅存在于用户级中,这种线程是不依赖于操作系统核心的。应用进程利用线程库来完成其创建和管理,速度比较快,操作系统内核无法感知用户级线程的存在。
10.什么是临界区,如何解决冲突?
每个进程中访问临界资源的那段程序称为临界区,一次仅允许一个进程使用的资源称为临界资源。
解决冲突的办法:
- 如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入,如已有进程进入自己的临界区,则其它所有试图进入临界区的进程必须等待;
- 进入临界区的进程要在有限时间内退出。
- 如果进程不能进入自己的临界区,则应让出CPU,避免进程出现“忙等”现象。
11.什么是死锁?死锁产生的条件?
什么是死锁:
在两个或者多个并发进程中,如果每个进程持有某种资源而又等待其它进程释放它或它们现在保持着的资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁。通俗的讲就是两个或多个进程无限期的阻塞、相互等待的一种状态。
死锁产生的四个必要条件:(有一个条件不成立,则不会产生死锁)
- 互斥条件:一个资源一次只能被一个进程使用
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得资源保持不放
- 不剥夺条件:进程获得的资源,在未完全使用完之前,不能强行剥夺
- 循环等待条件:若干进程之间形成一种头尾相接的环形等待资源关系
11(续).如何处理死锁问题
常用的处理死锁的方法有:死锁预防、死锁避免、死锁检测、死锁解除、鸵鸟策略。
(1)死锁的预防:基本思想就是确保死锁发生的四个必要条件中至少有一个不成立:
- ① 破除资源互斥条件
- ② 破除“请求与保持”条件:实行资源预分配策略,进程在运行之前,必须一次性获取所有的资源。缺点:在很多情况下,无法预知进程执行前所需的全部资源,因为进程是动态执行的,同时也会降低资源利用率,导致降低了进程的并发性。
- ③ 破除“不可剥夺”条件:允许进程强行从占有者那里夺取某些资源。当一个已经保持了某些不可被抢占资源的进程,提出新的资源请求而不能得到满足时,它必须释放已经保持的所有资源,待以后需要时再重新申请。这意味着进程已经占有的资源会被暂时被释放,或者说被抢占了。
- ④ 破除“循环等待”条件:实行资源有序分配策略,对所有资源排序编号,按照顺序获取资源,将紧缺的,稀少的采用较大的编号,在申请资源时必须按照编号的顺序进行,一个进程只有获得较小编号的进程才能申请较大编号的进程。
(2)死锁避免:
死锁预防通过约束资源请求,防止4个必要条件中至少一个的发生,可以通过直接或间接预防方法,但是都会导致低效的资源使用和低效的进程执行。而死锁避免则允许前三个必要条件,但是通过动态地检测资源分配状态,以确保循环等待条件不成立,从而确保系统处于安全状态。所谓安全状态是指:如果系统能按某个顺序为每个进程分配资源(不超过其最大值),那么系统状态是安全的,换句话说就是,如果存在一个安全序列,那么系统处于安全状态。银行家算法是经典的死锁避免的算法。
(3)死锁检测:
死锁预防策略是非常保守的,他们通过限制访问资源和在进程上强加约束来解决死锁的问题。死锁检测则是完全相反,它不限制资源访问或约束进程行为,只要有可能,被请求的资源就被授权给进程。但是操作系统会周期性地执行一个算法检测前面的循环等待的条件。死锁检测算法是通过资源分配图来检测是否存在环来实现,从一个节点出发进行深度优先搜索,对访问过的节点进行标记,如果访问了已经标记的节点,就表示有存在环,也就是检测到死锁的发生。
- (1)如果进程-资源分配图中无环路,此时系统没有死锁。
- (2)如果进程-资源分配图中有环路,且每个资源类中只有一个资源,则系统发生死锁。
- (3)如果进程-资源分配图中有环路,且所涉及的资源类有多个资源,则不一定会发生死锁。
(4)死锁解除:
死锁解除的常用方法就是终止进程和资源抢占,回滚。所谓进程终止就是简单地终止一个或多个进程以打破循环等待,包括两种方式:终止所有死锁进程和一次只终止一个进程直到取消死锁循环为止;所谓资源抢占就是从一个或者多个死锁进程那里抢占一个或多个资源。
(5)鸵鸟策略:
把头埋在沙子里,假装根本没发生问题。因为解决死锁问题的代价很高,因此鸵鸟策略这种不采取任何措施的方案会获得更高的性能。当发生死锁时不会对用户造成多大影响,或发生死锁的概率很低,可以采用鸵鸟策略。大多数操作系统,包括 Unix,Linux 和 Windows,处理死锁问题的办法仅仅是忽略它。
12.进程调度策略有哪几种?
- 先来先服务:非抢占式的调度算法,按照请求的顺序进行调度。有利于长作业,但不利于短作业,因为短作业必须一直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成了短作业等待时间过长。另外,对I/O密集型进程也不利,因为这种进程每次进行I/O操作之后又得重新排队。
- 短作业优先:非抢占式的调度算法,按估计运行时间最短的顺序进行调度。长作业有可能会饿死,处于一直等待短作业执行完毕的状态。因为如果一直有短作业到来,那么长作业永远得不到调度。
- 最短剩余时间优先:最短作业优先的抢占式版本,按剩余运行时间的顺序进行调度。 当一个新的作业到达时,其整个运行时间与当前进程的剩余时间作比较。如果新的进程需要的时间更少,则挂起当前进程,运行新的进程。否则新的进程等待。
- 时间片轮转:将所有就绪进程按 FCFS 的原则排成一个队列,每次调度时,把 CPU 时间分配给队首进程,该进程可以执行一个时间片。当时间片用完时,由计时器发出时钟中断,调度程序便停止该进程的执行,并将它送往就绪队列的末尾,同时继续把 CPU 时间分配给队首的进程。
- 时间片轮转算法的效率和时间片的大小有很大关系:因为进程切换都要保存进程的信息并且载入新进程的信息,如果时间片太小,会导致进程切换得太频繁,在进程切换上就会花过多时间。 而如果时间片过长,那么实时性就不能得到保证。
- 优先级调度:为每个进程分配一个优先级,按优先级进行调度。为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级。
13.进程有哪些状态?
进程一共有5种状态,分别是创建、就绪、运行(执行)、终止、阻塞。

- 运行状态就是进程正在CPU上运行。在单处理机环境下,每一时刻最多只有一个进程处于运行状态。
- 就绪状态就是说进程已处于准备运行的状态,即进程获得了除CPU之外的一切所需资源,一旦得到CPU即可运行。
- 阻塞状态就是进程正在等待某一事件而暂停运行,比如等待某资源为可用或等待I/O完成。即使CPU空闲,该进程也不能运行。
运行态→阻塞态:往往是由于等待外设,等待主存等资源分配或等待人工干预而引起的。 阻塞态→就绪态:则是等待的条件已满足,只需分配到处理器后就能运行。 运行态→就绪态:不是由于自身原因,而是由外界原因使运行状态的进程让出处理器,这时候就变成就绪态。例如时间片用完,或有更高优先级的进程来抢占处理器等。 就绪态→运行态:系统按某种策略选中就绪队列中的一个进程占用处理器,此时就变成了运行态。
14.什么是分页?
把内存空间划分为大小相等且固定的块,作为主存的基本单位。因为程序数据存储在不同的页面中,而页面又离散的分布在内存中,因此需要一个页表来记录映射关系,以实现从页号到物理块号的映射。
访问分页系统中内存数据需要两次的内存访问 (一次是从内存中访问页表,从中找到指定的物理块号,加上页内偏移得到实际物理地址;第二次就是根据第一次得到的物理地址访问内存取出数据)。

15.什么是分段?
分页是为了提高内存利用率,而分段是为了满足程序员在编写代码的时候的一些逻辑需求(比如数据共享,数据保护,动态链接等)。
分段内存管理当中,地址是二维的,一维是段号,二维是段内地址;其中每个段的长度是不一样的,而且每个段内部都是从0开始编址的。由于分段管理中,每个段内部是连续内存分配,但是段和段之间是离散分配的,因此也存在一个逻辑地址到物理地址的映射关系,相应的就是段表机制。

查看2道真题和解析