操作系统学习笔记 | 操作系统常见问题整理
- 计算机系统概述
- 什么是操作系统?有什么特点?
操作系统(Operating System, OS)是控制和管理计算机硬件与软件资源的程序,为应用程序和用户提供一个运行环境和接口。操作系统就像一个“公司经理”,负责安排每个员工(应用程序)用什么设备(资源)、什么时候工作(CPU调度),还要保障大家公平、安全、有序。
操作系统具有以下特点:
- 并发性:可以同时运行多个程序。
- 共享性:多个程序可以共享硬件资源。
- 虚拟性:通过抽象(如虚拟内存)让用户觉得每个程序独占资源。
- 异步性:程序执行时间不确定,可以暂停和恢复。
- 操作系统有哪些功能?
主要有五大功能:
- 进程管理:负责创建、调度、终止进程,确保多个程序公平、高效地使用CPU。类比酒店的运营系统安排客人(程序)什么时候用电梯(CPU)
- 内存管理:负责分配和回收内存,保证不同程序之间的内存互不干扰。类比酒店的运营系统为每个客人安排独立的房间(内存)。
- 文件管理:负责数据的存储、读取、修改,提供文件和目录的访问控制。类比酒店的运营系统管理酒店仓库(文件系统)中的物品。
- 设备管理:控制输入/输出设备(打印机、硬盘、网络等),提供统一的设备访问接口。类比酒店的运营系统统一调度共享设施(打印机、洗衣房)。
- 用户接口:提供与用户交互的环境(如命令行、图形界面)。类比酒店的运营系统提供前台、APP、电话预订。
- 并发和并行有什么区别?
并发(Concurrency)指的是指多个任务逻辑上同时进行,但可能轮流执行,系统通过快速切换任务,让人感觉它们是同时运行的。
并行(Parallelism)指的是多个任务物理上真正同时进行,通常发生在多核CPU上。
-
- 什么是异步?
异步是指发出请求后,发出请求后,不等待结果,继续执行其他任务,等结果准备好后再处理。这样的好处是可以提高效率,防止程序因等待而卡住。
同步就好像点餐后站在柜台等,不能干别的事。而异步就像点餐后去玩手机,餐做好后店员叫你。
-
- 什么是内核态和用户态?为什么要分两个态?
内核态(Kernel Mode)是操作系统自己运行的状态,有最高权限,可以访问所有硬件资源。。用户态(User Mode)是应用程序运行的状态,权限受限,不能直接访问硬件。分为内核态和用户态是为了安全性和稳定性,防止一个程序崩溃导致整个系统崩溃,以及防止用户程序误操作或恶意破坏硬件。
-
- 用户态和内核态是如何切换的?
用户态和内核态主要通过系统调用(System Call)进行切换。当用户程序需要操作硬件(比如读文件、网络通信)时,必须请求操作系统帮忙。切换流程如下:
- 用户程序发起系统调用(通过特殊指令,如 int 0x80)。
- CPU切换到内核态,执行系统功能(如读文件)。
- 完成后切回用户态,继续执行用户程序。
可以类比你要去财务处报销,首先要通过OA系统审批,获得领导许可,才可以进行报销。
-
- 什么是系统调用?
系统调用是用户程序请求操作系统服务的接口,是用户态进入内核态的唯一途径。常见的系统调用有:
- 文件操作(打开、读、写、关闭)。
- 进程控制(创建、终止进程)。
- 设备管理(读写磁盘、网络通信)。
- 内存分配。
- 进程管理
- 程序和进程的区别?
程序(Program)是一组静态的指令和数据,存储在磁盘中(还没运行)。进程(Process)是程序的一次执行过程,正在运行,拥有独立资源(如内存、寄存器等)。
程序像是菜谱,进程像是正在做菜的人。
-
- 进程状态的转换有哪些?
常见的进程状态有三种:
- 就绪(Ready):等待CPU执行。
- 运行(Running):正在被CPU执行。
- 阻塞(Blocked):等待资源(如I/O),暂时不能执行。
假设你去排队买奶茶,排队的时候是就绪态,点单过程是运行态,等奶茶制作是阻塞态。
-
- 什么是进程调度?有哪些进程调度算法?
进程调度指的是决定哪个进程获得CPU时间。常见的调度算法有:先来先服务(First-Come First-Served, FCFS)、短作业优先(Shortest Job First, SJF)、高响应比优先(Highest Response Ratio Next, HRRN)、时间片轮转(Round Robin, RR)、优先级调度(Priority Scheduling, PS)和多级反馈队列(Multilevel Feedback Queue, MLFQ)。
-
-
- 先来先服务(FCFS)
-
按照进程到达就绪队列的顺序执行,先到先服务,属于非抢占式调度。优点是公平,算法简单,不会产生饥饿;缺点是对短作业不友好,长作业会拖慢整体效率。比如,银行排队,客户先到先办理业务,哪怕第一个客户的业务非常复杂,后面的人也必须等待。
-
-
- 短作业优先(SJF)
-
优先执行运行时间最短的作业,属于非抢占式。优点是平均等待时间、周转时间最短;缺点是进程长度需提前知道;容易导致长作业饿死。比如,餐厅排队时,优先处理只点了一杯咖啡的顾客,大餐订单需要等。
-
-
- 高响应比优先(HRRN)
-
综合考虑等待时间和运行时间,选择响应比最高的进程,属于非抢占式。优点是兼顾长短作业,不会产生饥饿;缺点是调度时需要频繁计算响应比。
-
-
- 时间片轮转(RR)
-
按照进程到达顺序,分配固定时间片,轮流执行;如果时间片用完,未完成的进程被放回队列尾部,属于抢占式。优点是公平,响应快,适合分时系统;缺点是频繁切换有开销,不能区分任务紧急程度。比如,多人玩游戏,每人轮流玩5分钟,时间到了必须换人。
-
-
- 优先级调度(PS)
-
为每个进程设置优先级,优先级高的进程优先运行。优先级可以静态或动态调整,是抢占式或非抢占式。优点是支持紧急任务,适合实时系统;缺点是可能导致低优先级进程长期饿死。比如,急诊病人优先(优先级高),普通门诊病人稍后。
-
-
- 多级队列调度算法(MLFQ)
-
系统中按进程类型设置多个队列,进程创建成功后插入某个队列。队列之间采用固定优先级和时间片划分,即高优先级空时低优先级进程才能被调度。各队列可采用不同的调度策略,如系统进程队列采用优先级调度,交互式队列采用时间片轮转,批处理队列采用先来先服务。
-
- 什么是CPU利用率、周转时间、响应时间?
CPU利用率衡量CPU的繁忙程度。数值越高,说明CPU的利用越充分。
CPU 利用率=CPU 运行时间CPU 运行时间+CPU 空闲时间
系统吞吐量表示单位时间内,系统完成作业的数量。值越高,表示系统处理能力越强。
系统吞吐量=总共完成了多少道作业总共花了多少时间
周转时间表示一个作业从提交到完成所经历的总时间(包括等待、运行、I/O 等所有时间)。
周转时间=作业完成时间-作业提交时间
平均周转时间=各作业周转时间之和作业数
带权周转时间对作业的公平性衡量。值越小,说明这个作业等待时间少,系统响应更快。
带权周转时间=作业完成时间-作业提交时间作业实际运行的时间
平均带权周转时间=各作业带权周转时间之和作业数
等待时间是作业在队列里等待的时间,CPU 忙于其他作业时,当前作业处于等待状态。
等待时间=周转时间-运行时间-I/O操作的时间
响应时间指的是用户提交请求到系统第一次给出反馈所用的时间,重点衡量交互式系统的体验。
响应时间=从用户提交请求到首次产生响应所用的时间
-
- 什么是进程同步和互斥?
进程互斥指的是多个进程不能同时访问同一共享资源(如打印机),进程同步指的是多个进程必须按一定顺序协作(如生产者必须先生产,消费者才能消费)。
-
- 进程如何通信?
进程通信指的是两个或多个进程之间进行数据交换。由于进程是操作系统分配资源的基本单位,因此每个进程拥有独立的内存空间,彼此不能直接访问对方的数据。例如,进程A正在处理用户输入,进程B正在处理图像渲染,进程A无法直接读取进程B的内存地址。如果进程之间需要传递数据,必须通过进程间通信(IPC)机制,如消息队列、管道、共享内存等。高级通信包括以下三个部分:
- 共享存储
共享存储是进程间最快的通信方式,操作系统会在内存中划出一块共享区域,并将该区域映射到多个进程的虚拟地址空间。
假设进程A和进程B都处理一个图像编辑任务,它们通过一块共享内存区域交换图像数据。比如,进程A读取用户输入的像素修改请求,进程B实时渲染修改效果。
- 消息传递
消息传递是通过操作系统提供的系统调用来交换格式化的消息。
进程A(客户端)请求一个进程B(服务器)进行文件下载,进程A使用send原语发送一个消息,其中包含发送者ID、接收者ID、请求命令,进程B使用receive原语接收消息并返回下载结果。
- 管道通信
管道是一个特殊的共享文件,管道通信是基于内存缓冲区的数据流传输方式。
管道通信是一种先进先出的数据流,想象成一条水管。写入和读取都从缓冲区的头尾进行,数据流动方向明确。管道通信类似于排队的水管,数据先进先出,写数据有顺序,读数据也只能先读头部,再往后取。共享存储类似于开辟一块公共办公区,所有进程可以随意在里面读写数据,没有顺序而言。
当多个进程读同一个管道时,可能会错乱。对此,通常管道中的数据一旦被读出,就彻底消失。因此,有两种解决方案:① 一个管道允许多个写进程,一个读进程;② 允许有多个写进程,多个读进程,但系统会让各个读进程轮流从管道中读数据。
-
- 堆和栈有什么区别?
栈用于存储局部变量和函数调用信息,是系统自动管理的,访问速度较快,但空间较小;堆用于存储动态分配的内存,是程序员手动申请释放的,访问速度较慢,但空间较大。
-
- 线程和进程有什么区别?
线程是进程内部的最小执行单元,多个线程共享同一个进程的内存空间。不同进程的内存空间是相互独立的,而同一进程的线程共享内存。如果说进程是一个公司,那线程就是公司里的员工。
-
- 为什么要使用多线程?
- 提高CPU利用率。
- 提高程序响应速度。
- 更好地处理高并发场景。
好比一个厨房里多个人同时做菜,比一个人做菜效率高。
-
- 锁是什么?
锁是用来控制多线程访问共享资源的工具,防止数据冲突。好比厕所门锁,避免同时有人进。
-
- 自旋锁和互斥锁有什么区别?
自旋锁循环等待,占用CPU,适用于临界区代码执行时间较短的场景。互斥锁阻塞等待,不占CPU,适用于临界区代码执行时间较长的场景。若临界区较长(如涉及I/O操作、复杂计算),自旋锁会导致CPU资源浪费或优先级反转问题。
-
- 生产者消费者问题是什么?
生产者-消费者问题指的是一个生产者生产数据放入缓冲区,一个消费者从缓冲区取数据。需要保证缓冲区不能溢出,不能取空。
Semaphore mutex = 1;
Semaphore empty = N; // N 为缓冲区大小
Semaphore full = 0;
void producer() {
while (true) {
produce_item();
P(&empty); // 等待有空位置
P(&mutex); // 进入临界区
insert_item(); // 放入产品
V(&mutex); // 离开临界区
V(&full); // 增加已用缓冲区
}
}
void consumer() {
while (true) {
P(&full); // 等待有产品
P(&mutex); // 进入临界区
remove_item(); // 取走产品
V(&mutex); // 离开临界区
V(&empty); // 增加空缓冲区
consume_item();
}
}
-
- 什么是信号量?
信号量(Semaphore)是一个整型计数器,用于管理对共享资源的访问。主要分为两类:互斥信号量(Binary Semaphore / Mutex):取值0或1,专用于进程互斥。通用信号量(Counting Semaphore):取值为非负整数,用于控制资源数量。
-
- 什么是死锁?
多个进程互相等待对方释放资源,最终都无法继续执行。
-
- 产生死锁的条件有哪些?
以下四个条件同时成立才会产生死锁:
-
- 互斥:资源只能被一个进程占用。如打印机、文件、数据库锁。
- 不可剥夺:已分配的资源不能强行拿走。如进程A获得了打印机,不能被B抢走。
- 请求与保持:已持有资源的进程继续申请新资源。进程A占有打印机,等待硬盘;进程B占有硬盘,等待打印机。
- 循环等待:进程之间形成环状等待链。A等B,B等C,C等A。
-
- 预防死锁的方法有哪些?
死锁条件 | 破坏方法 | 例子 |
互斥条件 | 尽量使用可共享资源 | 只读文件、使用临界区内无互斥设计 |
不可剥夺条件 | 允许资源被强制回收 | CPU时间片可强制抢占 |
占有且等待条件 | 要求进程一次性申请全部资源 | printf打印时,先申请所有锁 |
循环等待条件 | 按统一顺序申请资源 | 数据库加锁时要求先锁表再锁行 |
-
- 避免死锁的方法有什么?
死锁避免指的是不盲目分配资源,通过算法预测资源分配后系统是否进入安全状态,如果不安全则拒绝请求。比如,银行有10单位资源,进程A最多需要7单位,现在占用5单位,A最多还需2单位。如果当前剩余资源不足以满足任何一个进程的最大需求,系统会拒绝后续请求,避免死锁。这种方法的优点是系统资源利用率较高,缺点是实时计算开销大,进程必须提前声明最大资源需求。
-
- 什么是银行家算法?
银行家算法是一种避免死锁的算法。其核心思想在于银行在借钱前会确认你有没有还款能力,只有保证不会造成风险时才会放贷。
- 内存管理
- 什么是动态重定位?
不同进程可以有相同的逻辑地址,因为这些相同的逻辑地址可以映射到主存不同位置。逻辑地址转换成物理地址的过程,称为地址重定位。地址重定位发生在程序运行时称为动态重定位。动态重定位的好处是程序可以在任何物理地址运行,支持程序动态加载、动态内存分配。
-
- 内存空间扩充的方法有哪些?
主要包括以下三种方法:
- 覆盖技术:将程序划分为几个部分,不同时驻留内存,按需加载。好比箱子空间不够,把暂时不用的东西先放一边。
- 交换技术:将暂时不用的进程移到硬盘,需要时换回来。好比把暂时用不到的大件搬到仓库。
- 虚拟存储器:通过分页或分段,将一部分内存放在磁盘上,按需调入。想象把硬盘当作备用仓库,用的时候随时取。
- 内存连续分配有哪些方式?
连续分配指的是为用户进程的分配必须是一个连续的内存空间,有单一连续分配、固定分区分配和动态分区分配三种方式。
-
-
- 单一连续分配
-
在单一连续分配方式中,内存被分为系统区和用户区。系统区通常位于内存的低地址部分,用于存放操作系统相关数据;用户区用于存放用户进程相关数据。
内存中只能有一道用户程序,用户程序独占整个用户区空间。用户进程 A 尽管只有那么大,但是它会独占整一个用户区。优点是实现简单,无外部碎片,缺点是只能用于单用户、单任务的操作系统中,有内部碎片。
内部碎片,分配给某进程的内存区域中,如果有些部分没有用上。外部碎片,是指内存中的某些空闲分区由于太小而难以利用。
-
-
- 固定分区分配
-
为了能在内存中装入多道程序,且这些程序之间又不会互相干扰,于是将整个用户空间划分为若干个固定大小的分区,在每个分区中只装入一道作业,这就是固定分区分配方式。其中,左图是分区大小相等的类别,右图则是分区大小不等的类别。
分区大小相等:缺乏灵活性,但是很适合用于用一台计算机控制多个相同对象的场合。比如,钢铁厂有 n 个相同的炼钢炉,就可把内存分为 n 个大小相等的区域存放 n 个炼钢炉控制程序。
分区大小不等:增加了灵活性,可以满足不同大小的进程需求。根据常在系统中运行的作业大小情况进行划分。比如,划分多个小分区、适量中等分区、少量大分区。
固定分区分配的优点是实现简单,无外部碎片。但缺点是当用户程序太大时,可能所有的分区都不能满足需求,且会产生内部碎片,内存利用率低。
-
-
- 动态分区分配
-
动态分区分配又称为可变分区分配。这种分配方式不会预先划分内存分区,而是在进程装入内存时根据进程的大小动态地建立分区,并使分区的大小正好适合进程的需要。因此系统分区的大小和数目是可变的。
动态分区分配没有内部碎片,但是有外部碎片。如果内存中空闲空间的总和本来可以满足某进程的要求但由于进程需要的是一整块连续的内存空间,因此这些“碎片”不能满足进程的需求。可以通过紧凑技术来解决外部碎片。
-
- 动态内存分区分配算法有哪些?
主要有首次适应算法(First Fit)、最佳适应算法(Best Fit)、最坏适应算法(Worst Fit)以及临近适应算法(Next Fit)。
-
-
- 首次适应算法
-
每次都从低地址开始查找,找到第一个能满足大小的空闲分区。
空闲分区以地址递增的次序排列。,每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区。进程 5 会对空闲分区链进行扫描,可见第一个分区就是满足大小的分区,因此进程 5 会被分配到第一个分区中。
-
-
- 最佳适应算法
-
由于动态分区分配是一种连续分配方式,为各进程分配的空间必须是连续的一整片区域。因此为了保证当“大进程”到来时能有连续的大片空间,可以尽可能多地留下大片的空闲区即,优先使用更小的空闲区。
空闲分区按容量递增次序链接。每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区。
空闲分区表 / 链按分区大小增序排列,因此进程 6 会被分配到第二个分区中,分配完第二个分区剩下 1 MB,此时又会重新排序,第二个分区变成第一个分区。
这种方法的缺点是,每次都选最小的分区进行分配,会留下越来越多的、很小的、难以利用的内存块。因此这种方法会产生很多的外部碎片。
-
-
- 最坏适应算法
-
为了解决最佳适应算法容易留下太多难以利用的小碎片,最坏适应算法在每次分配时优先使用最大的连续空闲区,这样分配后剩余的空闲区就不会太小,更方便使用。
和最佳适应算法同理,空闲分区按容量递减次序链接。每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区。
这种方法的缺点是,每次都选最大的分区进行分配,虽然可以让分配后留下的空闲区更大,更可用,但是这种方式会导致较大的连续空闲区被迅速用完。如果之后有“大进程”到达,就没有内存分区可用了。
-
-
- 邻近适应算法
-
首次适应算法每次都从链头开始查找的。这可能会导致低地址部分出现很多小的空闲分区,而每次分配查找时,都要经过这些分区,因此也增加了查找的开销。如果每次都从上次查找结束的位置开始检索,就能解决上述问题。
空闲分区以地址递增的顺序排列(可排成一个环链表)。每次分配内存时从上次查找结束的位置开始查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区。
首次适应算法每次都要从头查找,每次都需要检索低地址的小分区但是这种规则也决定了当低地址部分有更小的分区可以满足需求时会更有可能用到低地址部分的小分区,也会更有可能把高地址部分的大分区保留下来。邻近适应算法的规则可能会导致无论低地址、高地址部分的空闲分区都有相同的概率被使用,也就导致了高地址部分的大分区更可能被使用,划分为小分区,最后导致无大分区可用。
-
- 分页存储如何实现逻辑地址到物理地址的转换?
-
- 分段存储和分页存储有什么区别?
分页和分段是两种常见的内存管理方式,页是物理单位,操作系统为了内存管理而划分的固定大小的内存块。页大小固定且由操作系统决定,分页的用户地址空间是一维的,程序员只需提供一个逻辑地址,系统自动分离页号和页内偏移。
段是逻辑单位,用于满足用户的程序结构和需求,一个段通常表示一组逻辑相关的数据或代码(例如函数、数组等)。用户可见,程序员在编程时需要显式指定段名,且段长度不固定,依据程序设计而定。分段的用户地址空间是二维的,程序员在访问地址时,必须同时给出段号和段内地址。
-
- 什么是快表?
快表又称为联想寄存器,是一种访问速度比内存快很多的高速缓存,用来存放最近访问的页表项的副本,可以加速地址变换的速度。与此对应,内存中的页表常被称为慢表。此外,快表是一种专门的硬件,当进程切换的时候,快表的内容也会被清除。
-
- 页面置换算法有哪些?
当页面不够用时,操作系统需要把内存中的页面换出去。
-
-
- 最优页面置换算法(OPT)
-
每次置换时,总是选择未来最长时间不会被访问或者永远不会再被访问的页面进行置换。优点是理论上缺页率最低,缺点是需要知道未来的访问序列,实际中无法实现,只能作为衡量其他算法的参照标准。
-
-
- 先进先出页面置换算法(FIFO)
-
总是置换最早进入内存的页面,即排在队列最前的页面。缺点是容易出现 Belady 异常:增加物理块数量,缺页次数反而可能增加。
-
-
- 最近最久未使用算法(LRU)
-
每次置换时,选择距离当前时刻最近最久没有被访问的页面。需要记录每个页面上一次被访问的时间,或用时间栈、时间链表等结构来模拟。优点是缺页率比较低,实际效果接近最优算法,缺点是实现复杂,开销较大。
-
-
- 时钟置换算法(Clock)
-
最近最久未使用算法的近似实现,每个页面设置一个访问位(0 或 1),页面被访问时,访问位置为 1。页面置换时,页面按循环队列检查:如果访问位是 0,直接置换。如果访问位是 1,清 0,继续检查下一个页面。优点是简单,性能接近最近最久未使用算法,硬件支持良好。
-
-
- 改进型时钟算法
-
在时钟置换算法的基础上,增加一个 修改位(M 位),考虑页面是否被修改过。优先置换访问位和修改位为 0 的页面,如果没有,依次考虑访问位为 0,和修改位为 1,如果还是没有,继续扫描并清零访问位。优点是进一步降低误判,减少页面写回次数。
-
- 什么是抖动(颠簸)现象?
抖动(Thrashing)是指频繁地页面换入换出,导致CPU几乎全部时间都在等待页面调度,效率极低。像一个人有一堆书,但书桌太小,不断地拿书、放书,结果根本没时间看书。
-
- 什么是缓冲区?有什么作用?
缓冲区(Buffer)是内存中的临时存储区域,主要用于协调数据生产者和数据消费者之间的速度差异。作用包括弥补CPU和磁盘的读写速度差,减少频繁I/O操作,提高效率。可以类比餐厅的取餐台为缓冲区,厨师先把菜做好放在取餐台,顾客随时取。
- 文件管理
- 软链接和硬链接有什么区别?
硬链接是指给同一个文件数据增加一个新的入口,它直接指向文件的物理存储(inode)。文件的本质在 Linux 系统中是 inode(索引节点),inode 里记录了文件数据在磁盘的位置。硬链接创建后,多个文件名指向的是同一个 inode。就好比一个快递柜有多个钥匙,谁拿到钥匙都可以打开快递柜拿到同样的东西。钥匙丢了没关系,只要还有别的钥匙在,东西不会丢。
软链接更像是快捷方式或指针,它不是直接指向文件数据,而是存储一个路径字符串,这个路径指向目标文件。如果源文件被删除,软链接会变成“悬挂链接”,无法再访问文件内容。就好比你写了一个地址在纸条上,告诉别人去那个地址拿快递。结果如果那个快递已经被取走或者地址被撤销,纸条就没有用了。
- I/O管理
- 磁盘调度算法有哪些?
磁盘调度的目的是提高磁盘读写效率,减少磁头移动距离和寻道时间。
-
-
- 先来先服务(FCFS)
-
谁先来就先处理,缺点是磁头可能大幅度来回移动,效率低。就好比超市排队,谁先到就先结账,排队顺序不管远近。
-
-
- 最短寻道时间优先(SSTF)
-
每次选择离当前磁头最近的请求处理,优点是平均寻道时间短,但缺点是可能造成远处请求长期得不到处理(饥饿现象)。好比外卖骑手优先送最近的单,远的单可能迟迟送不到。
-
-
- 扫描算法(SCAN)
-
磁头朝一个方向移动,按顺序处理遇到的请求,走到边界后反方向继续扫描。优点是相对公平,减少磁头来回跳跃,缺点是边界可能等待时间较长。
-
-
- 循环扫描算法(C-SCAN)
-
磁头单方向扫描,走到尽头后立即回到起点,不处理回程请求。优点是服务等待时间更均匀,缺点是回程期间磁头空转。