跳转至

操作系统面试题(爪哇程序员)

说下进程的状态

原文:https://zwmst.com/1318.html

就绪:进程已处于准备好运行的状态,即进程已分配到除CPU外的所有必要资源后,只要再获 得CPU,便可立即执行

执行:进程已经获得CPU,程序正在执行状态

阻塞:正在执行的进程由于发生某事件(如I/O请求、申请缓冲区失败等)暂时无法继续执行 的状态

说下进程和线程的联系与区别

原文:https://zwmst.com/1320.html

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分 配和调度的一个独立单位

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本 单位

**进程和线程的关系

一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程是操作系 统可识别的最小执行和调度单位

资源分配给进程,同一进程的所有线程共享该进程的所有资源。 同一进程中的多个线程共享代 码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量

处理机分给线程,即真正在处理机上运行的是线程

线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步

**进程与线程的区别

进程有自己的独立地址空间,线程没有

进程是资源分配的最小单位,线程是CPU调度的最小单位

进程和线程通信方式不同(线程之间的通信比较方便。同一进程下的线程共享数据(比如全局变 量,静态变量),通过这些数据来通信不仅快捷而且方便,当然如何处理好这些访问的同步与 互斥正是编写多线程程序的难点。而进程之间的通信只能通过进程通信的方式进行。)

进程上下文切换开销大,线程开销小

一个进程挂掉了不会影响其他进程,而线程挂掉了会影响其他线程

对进程进程操作一般开销都比较大,对线程开销就小了

为什么进程上下文切换比线程上下文切换代价高?

原文:https://zwmst.com/1322.html

**进程切换分两步:

切换页目录以使用新的地址空间

切换内核栈和硬件上下文

对于linux来说,线程和进程的最大区别就在于地址空间,对于线程切换,第1步是不需要做 的,第2是进程和线程切换都要做的

**切换的性能消耗:

线程上下文切换和进程上下问切换一个最主要的区别是线程的切换虚拟内存空间依然是相同 的,但是进程切换是不同的。这两种上下文切换的处理都是通过操作系统内核来完成的。内核 的这种切换过程伴随的最显著的性能损耗是将寄存器中的内容切换出

另外一个隐藏的损耗是上下文的切换会扰乱处理器的缓存机制。简单的说,一旦去切换上下 文,处理器中所有已经缓存的内存地址一瞬间都作废了。还有一个显著的区别是当你改变虚拟 内存空间的时候,处理的页表缓冲(processor’s Translation Lookaside Buffer (TLB))或 者相当的神马东西会被全部刷新,这将导致内存的访问在一段时间内相当的低效。但是在线程 的切换中,不会出现这个问题

说下你对进程同步的理解

原文:https://zwmst.com/1324.html

进程同步的主要任务:是对多个相关进程在执行次序上进行协调,以使并发执行的诸进程之间 能有效地共享资源和相互合作,从而使程序的执行具有可再现性

**同步机制遵循的原则:

空闲让进;

忙则等待(保证对临界区的互斥访问);

有限等待(有限代表有限的时间,避免死等);

让权等待(当进程不能进入自己的临界区时,应该释放处理机,以免陷入忙等状态)

进程的通信方式有哪些

原文:https://zwmst.com/1326.html

进程通信,是指进程之间的信息交换(信息量少则一个状态或数值,多者则是成千上万个字 节)。因此,对于用信号量进行的进程间的互斥和同步,由于其所交换的信息量少而被归结为 低级通信

所谓高级进程通信指:用户可以利用操作系统所提供的一组通信命令传送大量数据的一种通信 方式。操作系统隐藏了进程通信的实现细节。或者说,通信过程对用户是透明的

**高级通信机制可归结为三大类:

共享存储器系统(存储器中划分的共享存储区);实际操作中对应的是“剪贴板”(剪贴板实际 上是系统维护管理的一块内存区域)的通信方式,比如举例如下:word进程按下ctrl+c,在ppt进程按下ctrl+v,即完成了word进程和ppt进程之间的通信,复制时将数据放入到剪贴 板,粘贴时从剪贴板中取出数据,然后显示在ppt窗口上

消息传递系统(进程间的数据交换以消息(message)为单位,当今最流行的微内核操作系统 中,微内核与服务器之间的通信,无一例外地都采用了消息传递机制。应用举例:邮槽

(MailSlot)是基于广播通信体系设计出来的,它采用无连接的不可靠的数据传输。邮槽是一 种单向通信机制,创建邮槽的服务器进程读取数据,打开邮槽的客户机进程写入数据

管道通信系统(管道即:连接读写进程以实现他们之间通信的共享文件(pipe文件,类似先进 先出的队列,由一个进程写,另一进程读))。实际操作中,管道分为:匿名管道、命名管 道。匿名管道是一个未命名的、单向管道,通过父进程和一个子进程之间传输数据。匿名管道 只能实现本地机器上两个进程之间的通信,而不能实现跨网络的通信。命名管道不仅可以在本 机上实现两个进程间的通信,还可以跨网络实现两个进程间的通信

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

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

消息队列:是一个在系统内核中用来保存消 息的队列,它在系统内核中是以消息链表的形式出 现的。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺 点

共享内存:共享内存允许两个或多个进程访问同一个逻辑内存。这一段内存可以被两个或两个 以上的进程映射至自身的地址空间中,一个进程写入共享内存的信息,可以被其他使用这个共 享内存的进程,通过一个简单的内存读取读出,从而实现了进程间的通信。如果某个进程向共 享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。共享内 存是最快的IPC方式,它是针对其它进程间通信方式运行效率低而专门设计的。它往往与其它 通信机制(如信号量)配合使用,来实现进程间的同步和通信

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

进程调度的种类有哪些?

原文:https://zwmst.com/1328.html

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

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

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

非抢占式调度与抢占式调度的区别是什么?

原文:https://zwmst.com/1330.html

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

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

说下你知道的调度算法

原文:https://zwmst.com/1332.html

**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等

一个程序从开始运行到结束的完整过程(四个过程)

原文:https://zwmst.com/1334.html

预处理:条件编译,头文件包含,宏替换的处理,生成.i文件。

编译:将预处理后的文件转换成汇编语言,生成.s文件

汇编:汇编变为目标代码(机器代码)生成.o的文件

链接:连接目标代码,生成可执行程序

死锁出现的条件?

原文:https://zwmst.com/1336.html

定义:如果一组进程中的每一个进程都在等待仅由该组进程中的其他进程才能引发的事件,那么 该组进程就是死锁的。或者在两个或多个并发进程中,如果每个进程持有某种资源而又都等待 别的进程释放它或它们现在保持着的资源,在未改变这种状态之前都不能向前推进,称这一组 进程产生了死锁。通俗地讲,就是两个或多个进程被无限期地阻塞、相互等待的一种状态。

**产生死锁的必要条件:

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

请求与保持条件(Hold and wait):已经得到资源的进程可以再次申请新的资源。

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

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

如何处理死锁问题

原文:https://zwmst.com/1338.html

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

检测死锁并且恢复。

仔细地对资源进行动态分配,使系统始终处于安全状态以避免死锁。

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

什么是临界资源

原文:https://zwmst.com/1341.html

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

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

介绍一下内存池、进程池、线程池

原文:https://zwmst.com/1343.html

首先介绍一个概念“池化技术 ”。池化技术就是:提前保存大量的资源,以备不时之需以及重 复使用。池化技术应用广泛,如内存池,线程池,连接池等等。内存池相关的内容,建议看看 Apache、Nginx等开源web服务器的内存池实现。

由于在实际应用当做,分配内存、创建进程、线程都会设计到一些系统调用,系统调用需要导 致程序从用户态切换到内核态,是非常耗时的操作。因此,当程序中需要频繁的进行内存申请 释放,进程、线程创建销毁等操作时,通常会使用内存池、进程池、线程池技术来提升程序的 性能。

线程池:线程池的原理很简单,类似于操作系统中的缓冲区的概念,它的流程如下:先启动若 干数量的线程,并让这些线程都处于睡眠状态,当需要一个开辟一个线程去做具体的工作时, 就会唤醒线程池中的某一个睡眠线程,让它去做具体工作,当工作完成后,线程又处于睡眠状 态,而不是将线程销毁。

进程池与线程池同理。

内存池:内存池是指程序预先从操作系统申请一块足够大内存,此后,当程序中需要申请内存 的时候,不是直接向操作系统申请,而是直接从内存池中获取;同理,当程序释放内存的时 候,并不真正将内存返回给操作系统,而是返回内存池。当程序退出(或者特定时间)时,内存 池才将之前申请的内存真正释放。

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

原文:https://zwmst.com/1345.html

静态库

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

动态库

静态库很方便,但是如果我们只是想用库中的某一个函数,却仍然得把所有的内容都链接进 去。一个更现代的方法则是使用共享库,避免了在文件中静态库的大量重复。 动态链接可以在首次载入的时候执行(load-time linking),这是 Linux 的标准做法,会由动 态链接器ld-linux.so 完成,比方标准 C 库(libc.so) 通常就是动态链接的,这样所有的程序可 以共享同一个库,而不用分别进行封装。 动态链接也可以在程序开始执行的时候完成(run-time linking),在 Linux 中使用 dlopen() 接口来完成(会使用函数指针),通常用于分布式软件,高性能服务器上。而且共享库也可以 在多个进程间共享。

链接使得我们可以用多个对象文件构造我们的程序。可以在程序的不同阶段进行(编译、载 入、运行期间均可),理解链接可以帮助我们避免遇到奇怪的错误。

区别:

使用静态库的时候,静态链接库要参与编译,在生成执行文件之前的链接过程中,要将静态链 接库的全部指令直接链接入可执行文件中。而动态库提供了一种方法,使进程可以调用不属于 其可执行代码的函数。函数的可执行代码位于一个.dll文件中,该dll包含一个或多个已被编 译,链接并与使用它们的进程分开储存的函数。 静态库中不能再包含其他动态库或静态库,而在动态库中还可以再包含其他动态或者静态库。 静态库在编译的时候,就将库函数装在到程序中去了,而动态库函数必须在运行的时候才被装 载,所以使用静态库速度快一些。

说下对虚拟内存的理解

原文:https://zwmst.com/1347.html

定义:具有请求调入功能和置换功能,能从逻辑上对内存容量加以扩充得一种存储器系统。其 逻辑容量由内存之和和外存之和决定。

与传统存储器比较虚拟存储器有以下三个主要特征: 多次性,是指无需在作业运行时一次性地全部装入内存,而是允许被分成多次调入内存运行。 对换性,是指无需在作业运行时一直常驻内存,而是允许在作业的运行过程中,进行换进和换出。 虚拟性,是指从逻辑上扩充内存的容量,使用户所看到的内存容量,远大于实际的内存容量。

**虚拟内存的实现有以下两种方式:

请求分页存储管理。

请求分段存储管理。

页面置换算法了解多少?

原文:https://zwmst.com/1349.html

操作系统将内存按照页面进行管理,在需要的时候才把进程相应的部分调入内存。当产生缺页 中断时,需要选择一个页面写入。如果要换出的页面在内存中被修改过,变成了“脏”页面,那 就需要先写会到磁盘。页面置换算法,就是要选出最合适的一个页面,使得置换的效率最高。 页面置换算法有很多,简单介绍几个,重点介绍比较重要的LRU及其实现算法。

**一、最优页面置换算法

最理想的状态下,我们给页面做个标记,挑选一个最远才会被再次用到的页面调出。当然,这 样的算法不可能实现,因为不确定一个页面在何时会被用到。

**二、先进先出页面置换算法(FIFO)及其改进

这种算法的思想和队列是一样的,该算法总是淘汰最先进入内存的页面,即选择在内存中驻留 时间最久的页面予淘汰。实现:把一个进程已调入内存的页面按先后次序链接成一个队列,并 且设置一个指针总是指向最老的页面。缺点:对于有些经常被访问的页面如含有全局变量、常 用函数、例程等的页面,不能保证这些不被淘汰。

**三、最近最少使用页面置换算法LRU(Least Recently Used)

根据页面调入内存后的使用情况做出决策。LRU置换算法是选择最近最久未使用的页面进行淘 汰。

1.为每个在内存中的页面配置一个移位寄存器。(P165)定时信号将每隔一段时间将寄存器右 移一位。最小数值的寄存器对应页面就是最久未使用页面。

2.利用一个特殊的栈保存当前使用的各个页面的页面号。每当进程访问某页面时,便将该页面 的页面号从栈中移出,将它压入栈顶。因此,栈顶永远是最新被访问的页面号,栈底是最近最 久未被访问的页面号。

中断与系统调用了解吗?

原文:https://zwmst.com/1351.html

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

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

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

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

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

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

典型的中断优先级如下所示: 机器错误 > 时钟 > 磁盘 > 网络设备 > 终端 > 软件中断

在讲系统调用之前,先说下进程的执行在系统上的两个级别:用户级和核心级,也称为用户态 和系统态(user mode and kernel mode)。 用户空间就是用户进程所在的内存区域,相对的,系统空间就是操作系统占据的内存区域。用 户进程和系统进程的所有数据都在内存中。处于用户态的程序只能访问用户空间,而处于内核 态的程序可以访问用户空间和内核空间。

用户态切换到内核态的方式有哪些?

原文:https://zwmst.com/1353.html

系统调用:程序的执行一般是在用户态下执行的,但当程序需要使用操作系统提供的服务时, 比如说打开某一设备、创建文件、读写文件(这些均属于系统调用)等,就需要向操作系统发 出调用服务的请求,这就是系统调用。

异常:当CPU在执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由 当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态,比如缺页异常。

外围设备的中断:当外围设备完成用户请求的操作后,会向CPU发出相应的中断信号,这时 CPU会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序,如果先前执 行的指令是用户态下的程序,那么这个转换的过程自然也就发生了由用户态到内核态的切换。 比如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后续操作等。

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

原文:https://zwmst.com/1355.html

权限不一样。

用户态的进程能存取它们自己的指令和数据,但不能存取内核指令和数据(或其他进程的指令 和数据)。

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

内部碎片与外部碎片分别是什么?

原文:https://zwmst.com/1357.html

在内存管理中,内部碎片是已经被分配出去的的内存空间大于请求所需的内存空间。

外部碎片是指还没有分配出去,但是由于大小太小而无法分配给申请空间的新进程的内存空间 空闲块。

固定分区存在内部碎片,可变式分区分配会存在外部碎片;

页式虚拟存储系统存在内部碎片;段式虚拟存储系统,存在外部碎片

为了有效的利用内存,使内存产生更少的碎片,要对内存分页,内存以页为单位来使用,最后 一页往往装不满,于是形成了内部碎片。

为了共享要分段,在段的换入换出时形成外部碎片,比如5K的段换出后,有一个4k的段进来 放到原来5k的地方,于是形成1k的外部碎片。

系统调用与库函数的区别

原文:https://zwmst.com/1359.html

系统调用(System call)是程序向系统内核请求服务的方式。可以包括硬件相关的服务(例如, 访问硬盘等),或者创建新进程,调度其他进程等。系统调用是程序和操作系统之间的重要接 口。

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

在移植性方面,不同操作系统的系统调用一般是不同的,移植性差;而在所有的ANSI C编译器 版本中,C库函数是相同的。

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

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

原文:https://zwmst.com/1361.html

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

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

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


我们一直在努力

apachecn/AiLearning

【布客】中文翻译组