# 操作系统面试题
# 什么的操作系统
操作系统是负责管理协调计算机硬件和软件资源的一种系统软件。其屏蔽了底层硬件的异构性和复杂性,为上层应用程序提供统一易用的接口.
# 主机启动
BIOS
:I/O 处理系统,开机时可以自动检测各种外设Bootloader
:加载 OS
# 中断和异常的处理过程
外中断是指由 CPU 执行指令以外的时间引起的,如 IO 完成中断、时钟中断、控制台中断;
内中断(异常)是由 CPU 执行指令内部事件引起的,如地址越界、除 0、算术溢出等.
硬件
- 设置中断标记
- 操作系统根据这个标记(中断号)去找到对应的处理程序
软件
- 保存现场:把当前执行程序的相关数据保存在寄存器中,然后入栈
- 开中断:以便于响应优先级更高的中断请求
- 中断服务程序处理
- 关中断:保证恢复现场时不被中断
- 恢复现场
# 系统调用
应用程序需要操作系统提供服务,而这些服务不能由应用程序直接执行。需要操作系统提供接 --- 系统调用
用户态
转化到内核态
# 操作系统功能
- 进程管理
- 内存管理:负责管理内存的分配、回收。在进程创建时分配内存以及在进程结束时回收内存,协调内存资源
- 设备管理
- 文件管理
- 提供用户接口
# CPU 内部结构
- 计算逻辑单元 ALU
- 寄存器
- 控制器
- 缓存
- 内存管理单元 MMU
# 寄存器
- 程序计数器
(prgram counter, PC)
:用于存放下一条运行指令的地址. - 指令寄存器
(Instraution Register, IR)
:用于存放当前正在执行的指令. - 程序状态字
(Program Status Word, PSW)
- 累加寄存器
(Accumulator Register, AX)
: - 基地寄存器
(Base Register, BX)
- 变址寄存器
# 进程与线程
进程是程序执行的一个过程,其实资源分配的基本单位,各个进程拥有自己独立的虚拟地址空间,执行互不干扰;同时父进程创建出的自进程之间也互不影响,子进程的崩溃或父进程的崩溃对互相并不影响。进程主要由程序段、数据段和程序控制块组成.
- 程序段:程序运行的代码
- 数据段:程序运行所产生的数据(全局变量、局部变量)
- 程序控制块:操作系统对该进程进行管理所涉及到的各种信息
PID
:进程标识符- 进程状态
- 进程优先级
- 程序计数器 PC
- 内存指针
- 上下文数据
在
Linux
中通过fork
函数来创建一个子进程,子进程会拷贝父进程的;同一个进程共享堆、全局变量、静态变量,但是线程独占栈、程序计数器
线程是进程内部的一个控制序列,其是 CPU
资源调度的基本单位,其是在进程内部运行,本质是在进程的地址空间运行,其可以和其他线程共享该进程的一些资源,比如全局变量、堆空间。但是每个线程也有自己独立的线程 ID,栈空间和程序计数器.
# 多线程与多进程
多进程的优点:
- 各个进程拥有自己独立的虚拟地址空间,各个程序之间的执行互不干扰,而且子进程的崩溃不会影响父进程,反之父进程崩溃也不会影响子进程
- 多个进程可以充分利用 CPU,并行执行程序,不用担心并行执行程序导致的问题
多线程的缺点:
- 由于进程的独立性,不同进程之间数据交换需要进程通信
- 创建一个子进程的开销比创建一个子线程的开销大
- 线程的切换比进程的切换系统开销会更大【切换页表、切换内核栈和硬件上下文(进程切换之后,新程序的虚拟地址在 TLB 内失效,会导致频繁的访存)】
多线程的优点:
多线程的缺点:
# 进程调度算法
- 先来先服务
FCFS(First Come First Serverd)
:按照请求的顺序进行调度(不利于短作业,不会导致饿死) - 短作业优先
SJF(Shortest Job First)
:按估计运行时间最短的顺序进行调度(不利于长作业,会导致饿死) - 最短剩余时间有优先
- 时间片轮转
- 优先级调度
- 多级反馈队列:在一定程度上减少了频繁的进程切换的开销.
# 进程状态切换
# 进程间通信 IPC 方式
两个或多个进程之间产生的数据交互
管道:半双工通信,数据只能单向流动.
在内核中开辟一块缓冲区,进程 A 把数据从用户区拷贝到内核缓冲区,进程 B 再从内核缓冲区把数据读走.
匿名管道:具有情缘关系的进程之间的通信方式(具有同一个祖先)
#include <unistd.h>
int pipe(int pipefd[2]);
/*
1. pipefd [0]: 读端 read (管道空时堵塞)
2. pipefd [1]: 写端 write (管道满时堵塞)
*/
有名管道:不相干的两个进程之间的通信方式
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *path, mode_t mode);
/*
path: 创建的命名管道的全路径名:
mod: 为指定了文件的读写权限;
*/
共享内存
(Shared Memory Segment)
:由一个进程创建,多个进程都可以访问的一段内存空间。通过shmat
可以将共享内存空间关联到指定的进程地址空间中消息队列
Socket
套接字:适用于不同主机之间的通信,也可以用于同一主机两个进程之间的通信信号
Signal
信号量
# 进程同步
- 互斥锁 + 条件变量
- 信号量
- 管程
# 多线程模型
- 用户级线程
# 锁机制
- 读写锁
- 互斥锁
- 条件变量
- 自旋锁
# 死锁
死锁是两个或两个以上的线程在执行的过程中,去争夺同一个共享资源锁导致的互相等待的一个现象,在没有外部干预的情况下,这些线程会一直处于堵塞状态,无法往下去执行.
死锁产生的四个条件:
- 互传条件
- 请求与保持条件
- 不剥夺条件
- 循环等待条件
# 防止死锁的方法
- 在第一次执行的时候一次性申请所有的共享资源
- 占用部分资源的进程在进一步去申请其他共享资源的时候,如果申请不到,就主动释放它所占有的资源
# 银行家算法
# 连续内存分配
单一连续分配
内存中只有一道用户程序用户独占整个用户区空间,无外部碎片,有内部碎片;可以使用覆盖技术进行逻辑扩容,不需要采用内存保护
固定分区分配(无外部碎片,会产生内部碎片)
- 分区大小相等(固定):缺乏灵活性
- 分区大小不等(固定)
动态内存分配:不会预先划分内存空间
- 首次适应算法:每次都从地地址开始查找,找到第一个能满足大小的空闲分区
- 最佳适应算法:选择尽可能小的内存分区分配给进程;空闲分区按照容量递增次序链接,每次分配内存时按顺序查找空闲分区链,找到第一个能满足要求的空闲分区
- 最坏适应算法:每次分配时使用最大的空闲内存区,为了过多的内存碎片;空闲分区安容量递减的次序链接,每次分配内存时按顺序查找空闲分区链,找到第一个能过满足要求的空闲分区
- 邻近适应算法:
首次适应算法
每次都要从链表头开始查找,这可能会导致低地址部分出现很多小的空闲分区,每次查找时需要进过这些分区,增加了查找开销。如果每次都从上次查找结束的位置开始检索,就能解决这个问题.
# 空闲内存的管理方式
- 空闲链表
- 位图:将内存划分为均等的分配单元,每个分配单元对应于位图中的一位,0 表示空闲,1 表示占用;分配单元的大小是一个值得考量的问题,分配单元太小会使得位图占用的空间过大,如果分配单元过大,内造成内部碎片。当需要分配一定大小空间的内存时,需要遍历真个位图,依次检查,时间复杂度较高.
# 内存紧缩与交换式碎片整理
# 非连续内存分配
- 连续内存空间分配,需要给程序分配连续空间,同时会产生外部碎片,内存利用率不高
- 非连续内存分配优点:
- 程序的物理地址空间是非连续的,更好的利用内存空间
- 允许共享代码与数据
- 支持动态加载和动态链接
- 非连续内存分配缺点:建立虚拟地址空间到物理地址空间的转换
分段
- 分段的寻址方式
分页
- 逻辑页
page
和物理页frame(帧)
的大小一致 - 不是所有的页都有对应的帧
- 逻辑页
分段与分页的区别
- 分页对程序员是透明的,但是分段需要程序员显示的划分每个段
- 页的大小不可改变,段的大小可动态变化
# 页表
标志位
- 访问位:表示当前页之前是否被访问过
- 修改位:表示当前页之前是否被修改过
- 保护位:表示是否允许对该页做任何类型的操作(读、写、可执行等)
- 驻留位:表示该页是在
内存
中还是在外存
中
帧号
- 由于逻辑空间很大,导致程序对应的页表会很大,无法存储在 CPU 内,所以页表一般放置在内存中,如没有任何其他优化机制,使用分页存储访问一个内存空间需要 2 次访存.
- 时间优化:TLB
- 空间优化:多级页表、反向页表
# TLB
TLB(Translation Look-aside Buffer)
:在 CPU 内的 MMU 中,用于缓存近期访问的页帧转换表项;使用相关存储器实现,时间局部性原理;若 TLB 命中则只需要一次访存,若 TLB missing,则需要两次访存.
# 多级页表
# 反向页表
# 分段与分页对比
# 覆盖技术
需要程序员自己把挣个程序划分为若干个小的功能模块,并确定各个模块之间的覆盖关系,增加程序员的负担
# 交换技术
以程序作为交换单位,需要把进程的整个地址空间都换进换出,增加了处理器的开销
# 虚拟技术
虚拟技术是把一个物理实体转化为多个逻辑实体
时空复用技术
多进程与多线程:多个进程能在同一个处理器上并发执行使用了时空复用技术,当每个进程轮流占用 CPU
空分复用技术
虚拟内存:将物理内存抽象为逻辑地址空间,每个进程都有各自的地址空间.
# 虚拟内存
- 将物理空间扩充为更大的逻辑空间
- 在装入程序时,不必将其全部装入内存,而只需将当前需要执行的部分页面或段装入内存,就可以让程序开始执行
- 在程序执行过程中,如果执行的指令或访问的数据不在内存中(
缺页
或缺段
),则由处理器通知操作系统将相应的页面或段调入内存,然后继续执行程序- 另一方面,操作系统将内存中暂时不适用的页面或段调出保存到磁盘中,从而腾出更多的空闲空间存放将要装入的程序
# 缺页中断处理过程
# 页面置换算法
当缺页中断发生时,需要调入新的页面而内存已满时,选择内存当中那个物理页面进行替换(更可能减少换入换出的次数)
最优页面置换算法:将未来最久不会被访问的页面置换出去(理想情况)
先进先出算法
最近最久未使用
LRU,Least Recently Used
- 时钟页面置换算法
CLOCK
:最近未用算法
- 时钟页面置换算法
二次机会法
最不常用算法
LFU,Least Frequently Used
Belady
现象:分配的物理页面数增加,缺页率反而提高的异常现象(没有考虑进程访问的动态特征导致的,FIFO
)
# 虚拟地址转换位物理地址的过程
# malloc
申请内存时操作系统会怎么做?
malloc
会调用 brk
和 mmap
两个系统调用来实现.
# 磁盘调度算法
# 参考资料
- [1] 王道计算机考研 操作系统_哔哩哔哩_bilibili
- [2] 清华 操作系统原理_哔哩哔哩_bilibili
- [3] 操作系统常见面试题
- [4] 这 50 道操作系统面试题,真牛批!