面经-操作系统
操作系统
进程和线程的区别
进程是资源分配和调度的基本单位。windows系统中的一个.exe就是一个进程
线程是程序执行的最小单位,一个进程可以运行多个线程,这些线程共享同一块内存。
资源开销:
- 进程:由于每个进程都有独立的内存空间,进程间切换需要保存和恢复整个进程的状态,因此上下文切换的开销较高。
- 线程:线程共享相同的内存空间,线程间切换只需要保存和恢复少量的线程上下文,因此上下文切换的开销较小。
通信与同步:
- 进程:由于进程间相互隔离,进程之间的通信需要使用一些特殊机制,如管道、消息队列、共享内存等。
- 线程:由于线程共享相同的内存空间,它们之间可以直接访问共享数据,线程间通信更加方便。
安全性:
- 进程:由于进程间相互隔离,一个进程的崩溃不会直接影响其他进程的稳定性。
- 线程:由于线程共享相同的内存空间,一个线程的错误可能会影响整个进程的稳定性。
并行和并发有什么区别
并行是指在同一时刻执行多个任务,每个任务都在不同的处理单元(如多个CPU核心)上执行。
并发是指在相同的时间段内执行多个任务,这些任务不是同时发生的,而是交替执行,通过时间片轮转或者事件驱动的方式。
解释一下用户态和内核态,什么场景下,会发生内核态和用户态的切换?
- 用户态和内核态的区别
用户态和内核态是操作系统为了保护系统资源、实现权限控制而设计的两种不同的CPU运行级别。
- 用户态:用户态的特点是,在用户态下,进程只能访问受限的资源和执行受限的指令集。
- 内核态:内核态的特点是,在内核态下,进程可以执行特权指令和访问操作系统的核心部分。执行诸如访问硬件资源,执行系统调用,内存、文件系统管理等操作。
- 在什么场景下,会发生内核态和用户态的切换
- 系统调用:当用户程序需要请求操作系统提供的服务时,会通过系统调用进入内核态。
- 异常:当用户程序执行过程中出现错误或异常情况时,CPU会自动切换到内核态,以便操作系统能够处理这些异常。
- 中断:外部设备(如键盘、鼠标等)产生的中断信号会使CPU从用户态切换到内核态。操作系统会处理这些中断,然后再将CPU切换回用户态。
进程调度算法
进程调度算法有基础和进阶之分。
基础
- 先来优先:抢占式的调度算法,按照请求的顺序进行调度。但是会导致较长作业阻塞较短作业。
- 最短作业优先:非抢占式的调度算法,按估计运行时间最短的顺序进行调度。 但是如果一直有短作业到来,那么长作业永远得不到调度,造成长作业“饥饿”现象。
- 最短剩余时间优先:前两种调度算法的结合,在遵循按照请求的顺序进行调度的前提下,以剩余运行时间的顺序作为调度标准。当一个新的作业到达时,其整个运行时间与当前进程的剩余时间作比较。如果新的进程需要的时间更少,则挂起当前进程,运行新的进程。否则新的进程等待。
进阶
- 优先级调度:为每个进程分配一个优先级,按优先级进行调度。等待进程的优先级会随着时间的推移增加,以防止低优先级的进程永远等不到调度。
- 时间片轮转:为每个进程分配一个时间片,进程轮流执行,时间片用完后切换到下一个进程。
- 多级队列:前两种调度算法的结合。 将进程分为不同的优先级队列,每个队列有自己的调度算法。
进程间有哪些通信方式
进程有五种通信方式
- 管道:是一种半双工的通信方式,只能在具有父子进程关系的进程间使用。
- 命名管道(FIFO): 类似管道,也是半双工的通信方式,但是它允许在不相关的进程间通信。
1 | # 比如linux中,中间的"|"就是管道 |
- 消息队列:消息队列是消息的链表,允许进程发送和接收消息,可以设置消息优先级。
1 | # 消息队列通信过程中,存在用户态与内核态之间的数据拷贝开销。 |
- 共享内存:共享内存是由一个进程映射创建的,能被其它进程访问的内存。它是最快的进程通信方式,很好地解决了消息队列中用户态和内核态数据相互拷贝的时间开销问题。
虚拟内存-操作系统通过硬盘空间模拟的额外内存,物理内存-内存

- 信号量:是一个计数器,它表示系统中某种资源的数量或者状态,常作为一种锁机制,是进程间以及同一进程内不同线程之间的同步手段。
- 信号:用于发送通知到进程,告知其发生了某种事件或条件。
1 | # linux结束其它进程的 Ctrl+C 就是一个信号 |
Socket
套接字:以上都是主机内的进程通信,如果要进行不同主机内的进程通信,就要用socket,它是支持TCP/IP 的网络通信的基本操作单元,ip地址代表主机,端口号代表应用。

解释一下进程同步和互斥,以及如何实现进程同步和互斥
进程同步是指协调多个并发执行的进程之间的执行顺序,以确保它们按照一定的顺序或时间间隔执行。
互斥指的是在某一时刻只允许一个进程访问某个共享资源。
实现进程同步和互斥的方式主要有两种:
其中一种常见的方法是使用信号量和 PV 操作。信号量是一种计数器,它表示系统中某种资源的数量或者状态(0,1;0为可用,1为不可用)。PV 操作是一种对信号量进行增加或者减少的操作,它们可以用来控制进程之间的同步或者互斥。
- P操作:probe,用于申请资源。
- V操作:vacate,用于释放资源。
另外一种方式是设置临界区,互斥锁和条件变量:
具体操作是把可能引发互斥问题的代码段放置到临界区。进入这个临界区前需要先获取互斥锁,退出临界区后释放互斥锁锁。这确保同一时间只有一个进程可以进入临界区。
- 条件变量:条件变量用于在进程之间传递信息,在其它进程占用临界区时休眠进程,在其它进程释放临界区后唤醒进程。
什么是死锁,如何预防死锁?
死锁是一种多进程或多线程因资源竞争而导致程序停滞的情况。(例如胡同口堵车)
死锁只有同时满足以下四个条件才会发生:
- 互斥使用:一个进程占用了某个资源时,其他进程无法同时占用该资源。
- 不可抢占:其他进程不可抢占持有者的资源,只能由持有者自愿释放占有的资源。
- 占有且等待:一个进程因为请求其它资源而阻塞的时候,不会释放自己占有的资源。
- 循环等待条件:多个进程之间形成一个循环等待资源的链,每个进程都在等待下一个进程所占有的资源。
避免死锁:通过破坏死锁的四个必要条件之一来预防死锁。一般不会对互斥使用进行破坏,如打印机只能单个进程使用,进程排队。
破坏不可抢占,一个进程因为请求其它资源而阻塞的时候,会释放自己占有的资源。
破坏占有且等待,每个进程一次申请所有它所需要的资源,如果缺少,就等待。
破坏循环等待条件,给每个资源标上序号,让所有进程按照相同的顺序请求资源。(这样请求到后面资源的进程就不会需要前面的资源。)
虚拟内存
虚拟内存是指在每一个进程创建加载的过程中,分配一个连续虚拟地址空间,它不是真实存在的,而是通过映射与实际物理地址空间对应。
虚拟内存的特点
内存方面
- 内存扩展: 虚拟内存使得每个程序都可以使用比实际可用内存更多的内存,从而允许运行更大的程序或处理更多的数据。
- 内存隔离:虚拟内存提供了进程之间的内存隔离。每个进程都有自己的虚拟地址空间,因此一个进程无法直接访问另一个进程的内存。
交互方面
- 物理内存管理:虚拟内存允许操作系统动态地将数据和程序的部分加载到物理内存中,以满足当前正在运行的进程的需求。
- 页面交换:当物理内存不足时,操作系统可以将一部分数据从物理内存写入到硬盘的虚拟内存中,这个过程被称为页面交换。
文件映射方面
- 虚拟内存映射文件:虚拟内存还可以用于将文件映射到内存中,这使得文件的读取和写入可以像访问内存一样高效。
线程同步的方式
线程同步机制是指在多线程编程中,保证线程之间的互不干扰的一种机制。常见的线程同步机制有以下四种:
- 互斥锁:互斥锁是一种最常见的锁类型,用于实现互斥访问共享资源。
- 读写锁: 允许多个线程同时读共享资源,只允许一个线程进行写操作。分为读(共享)和写(排他)两种状态。
- 条件变量:条件变量用于在线程之间传递信息,在其它线程占用临界区时休眠线程,在其它线程释放临界区后唤醒线程。
- 信号量:是一个计数器,它表示系统中某种资源的数量或者状态,也是不同线程之间的同步手段。
几种典型的锁
基础锁:
- 互斥锁:互斥锁是一种最常见的锁类型,用于实现互斥访问共享资源。
- 自旋锁:自旋锁是一种基于忙等待的锁,即线程在尝试获取锁时会不断轮询(一直申请访问),直到锁被释放。
其他的锁都是基于这两种锁的 ,常见的有:
- 读写锁:允许多个线程同时读共享资源,只允许一个线程进行写操作。分为读(共享)和写(排他)两种状态。
- 悲观锁:认为多线程同时修改共享资源的概率比较高,所以访问共享资源时候要上锁
- 乐观锁:认为多线程同时修改共享资源的概率比较低,所以先共享资源,如果出现同时修改的情况,再放弃本次操作。
页面置换算法
常见页面置换算法有最佳置换算法(OPT)、先进先出(FIFO)、最不经常使用、最近最久未使用算法(LRU)、时钟算法(Clock) 等。
- 最佳置换算法: 该算法根据未来的页面访问情况,选择最长时间内不会被访问到的页面进行置换。但操作系统不会知道未来的页面访问情况,所以这种算法只是一种理想情况下的置换算法。
- 先进先出
FIFO
算法:也就是最先进入内存的页面最先被置换出去。 - 最不经常使用
LFU
:基于页面的使用历史,选择访问次数最少的页面进行置换。 - 最近最久未使用算法
LRU
:LRU算法基于页面的使用历史,选择最久未被使用的页面进行置换。 - 时钟算法
CLOCK
:Clock算法的核心思想是通过使用一个指针(称为时钟指针)在环形链表上遍历,检查页面是否被访问过, 访问过的页面的访问位设为1,指针不需移动;当需要进行页面置换时,那么从指针的位置开始遍历环形链表。- 如果当前页面的访问位为0,表示该页面最近未被访问,可以选择进行置换。并将访问位设置为1。
- 如果当前页面的访问位为1,表示该页面最近被访问过,它仍然处于活跃状态。将访问位设置为0,并继续遍历下一个页面,直到遍历到一个访问位为0的页面,选择该页面进行置换。
select、poll、epoll的区别
I/O多路复用通常通过select、poll、epoll等系统调用来实现,解决同步非阻塞io下系统调用频繁的问题。
- select: select是一个最古老的I/O多路复用机制,它可以监视多个文件描述符的可读、可写和错误状态。然而,但是它的效率可能随着监视的文件描述符数量的增加而降低。

- poll: poll是select的一种改进,它使用轮询方式来检查多个文件描述符的状态,避免了select中文件描述符数量有限的问题。但对于大量的文件描述符,poll的性能也可能变得不足够高效。

- epoll: epoll是Linux特有的I/O多路复用机制,相较于select和poll,它在处理大量文件描述符时更加高效。epoll使用事件通知的方式,只有在文件描述符就绪时才会通知应用程序,而不需要应用程序轮询。


I/O多路复用允许在一个线程中处理多个I/O操作,避免了创建多个线程或进程的开销,即并发。
总结:
select
是最早的 I/O
多路复用技术,但受到文件描述符数量和效率方面的限制。poll
克服了文件描述符数量的限制,但仍然存在一定的效率问题。epoll
是一种高效的I/O多路复用技术,尤其适用于高并发场景,但它仅在 Linux
平台上可用。
一般来说,epoll
的效率是要比 select
和 poll
高的,但是对于活动连接较多的时候,由于回调函数触发的很频繁,其效率不一定比 select
和 poll
高。所以 epoll
在连接数量很多,但活动连接较小的情况下性能体现的比较明显。