2026-04-10 更新于 2026-04-11

操作系统笔记

关于操作系统的笔记。

第一章:操作系统引论

第一部分:操作系统是什么?它有什么特征?

可以把计算机硬件(CPU、内存、硬盘)想象成一家拥有各种精密机床的超级工厂,而操作系统(OS)就是这家工厂的大管家(或CEO)

1. 操作系统的作用与功能

没有OS的计算机就是一堆废铁,普通人根本无法使用。OS的作用主要体现在三个层面:

  1. 作为用户和计算机硬件之间的接口(对外): 不需要用二进制代码去控制硬盘磁头读取数据,只需要双击鼠标打开一个文件。OS把复杂的硬件操作封装成了简单的命令。

  2. 作为计算机系统资源的管理者(对内): 工厂里有很多任务要干,但资源有限。OS的四大主要功能(1.5节)就是管好这些资源:

    • 处理机管理(管CPU): 决定现在哪个程序可以使用CPU运行。

    • 存储器管理(管内存): 决定哪个程序的数据可以放在内存里,放在哪里,谁也不能越界。

    • 设备管理(管I/O): 管好键盘、鼠标、打印机等设备的输入输出。

    • 文件管理(管数据): 把硬盘上的物理数据块组织成能看懂的文件夹和文件。

  3. 扩充裸机(抽象): 在裸机(纯硬件)上覆盖一层软件,把它变成一台功能更强大、更容易使用的“虚拟机”。


2. 操作系统的四大基本特性 (1.3节 - 🌟 极其重要,考研和面试必考)

这是OS区别于普通软件(比如微信、Word)的最核心特征。

  • ① 并发 (Concurrency) - 最难理解的痛点!

    • 原理: 指两个或多个事件在同一时间间隔内发生。在宏观上看,好像有多个程序在同时运行;但在微观上(单核CPU),任何一个时刻其实只有一个程序在运行,CPU只是在它们之间极速切换,制造了“同时”的假象。

    • ⚠️ 易混淆风险: 必须区分并发 (Concurrency)并行 (Parallelism)。并行是指两个或多个事件在同一时刻发生(比如多核CPU,每个核同时跑一个程序)。

  • ② 共享 (Sharing)

    • 原理: 系统中的资源可供内存中多个并发执行的进程共同使用。

    • 分为两种:互斥共享(比如打印机,我用的时候你不能用,必须排队)和同时访问(比如硬盘上的一段公共代码,多个程序可以宏观上“同时”读取)。

  • ③ 虚拟 (Virtualization)

    • 原理: 把一个物理上的实体,变成若干个逻辑上的对应物。

    • 例子: 物理上只有一个CPU,但通过操作系统的“时分复用”技术(大家轮流快速用),每个程序都觉得自己独占了一个CPU。物理内存只有8GB,但通过“空分复用”技术,可以跑总计几十GB的程序。

  • ④ 异步 (Asynchrony)

    • 原理: 因为系统中有多个程序在并发执行,大家都在争抢资源。所以程序不是一气呵成跑完的,而是走走停停。每个程序何时开始、何时暂停、何时结束,以怎样的速度推进,都是不可预知的


第二部分:操作系统是如何演进的?它的底层运行机制是什么?

1. 发展过程:是被“逼”出来的 (1.2节)

操作系统的发展史,本质上是一部“如何榨干CPU每一滴性能”的血泪史。只需要记住几个关键节点的突破:

  • 单道批处理 -> 多道批处理系统 :这是质的飞跃。

    • 痛点: 以前CPU很快,但输入输出(I/O,比如读写磁带)极慢。如果一个程序在等I/O,CPU就只能干瞪眼(空闲)。

    • 解决原理(引入并发): 内存里同时放多个程序。程序A等I/O的时候,OS立刻把CPU切给程序B用。这就是我们上一节说的并发的起源。目的只有一个:提高资源利用率

  • 多道批处理 -> 分时系统 :为了“人机交互”。

    • 痛点: 批处理系统虽然效率高,但用户把任务交上去后就失去控制权了,没法在中途修改。

    • 解决原理(时间片轮转): 像切蛋糕一样把CPU时间切成极短的“时间片”(比如几十毫秒)。每个用户轮流用一个时间片。这就好比你在电脑上挂着后台脚本,同时还能流畅地打游戏,因为系统在极速切换,给你一种你独占电脑的错觉。


2. 操作系统的运行环境 (1.4节 - 🌟 本章最核心原理)

这里我们要解释一个极其重要的风险问题:如何防止用户写的烂代码(或恶意代码)把整个系统搞崩溃?

为了安全,CPU在硬件级别提供了支持,这就是双重工作模式 (1.4.3)

  • 用户态(目态): 这是普通应用程序(比如你写的Vue前端代码、或者跑的Python脚本)运行的环境。在这个状态下,CPU只能执行非特权指令(比如加减乘除)。

  • 核心态(管态/内核态): 这是操作系统内核运行的环境。在这个状态下,CPU可以执行特权指令(比如清空内存、直接控制硬盘读写、设置时钟)。

⚠️ 关键原理与规则: 如果用户态的程序企图执行特权指令(比如直接去读写别人的内存区域),CPU硬件会立刻发现,并强制停止该程序,防止系统被破坏。

那么问题来了,用户程序在用户态,操作系统在核心态。如果用户程序真的需要读写硬盘(比如保存一个文件),它自己又没有权限,怎么办?
这就必须依赖1.4.4 中断与异常

你之前如果玩过类似ESP32这样的单片机硬件开发,对“中断”的概念一定不陌生。在操作系统中:

  • 中断 (Interrupt) 是操作系统夺回CPU控制权的唯一途径!

  • 广义的中断分为两种:

    1. 外中断(狭义的中断): 来自CPU外部的硬件信号。比如你敲击了一下键盘,或者定时器到点(时间片用完)了。

    2. 内中断(异常/Exception): 来自CPU内部执行指令时产生的问题。比如程序算术除以0,或者程序主动发起特殊的指令请求操作系统帮忙(这叫“陷入指令”或Trap)。

核心铁律:只要发生中断或异常,CPU会立刻暂停当前的用户程序,将工作模式从“用户态”自动切换到“核心态”,然后把控制权交给操作系统的中断处理程序。


第二章:进程的描述与控制

1. 为什么要有“进程(Process)”?它和“程序(Program)”有什么区别?

  • 程序:是静态的。它就是你写完后放在硬盘里的一堆代码文件(比如你写的那些Vue组件、或者编译好的 C++ 可执行文件)。它是一个没有生命的实体

  • 进程:是动态的。当你双击运行这个程序,操作系统把它加载到内存中,并分配了资源开始执行时,它就成了一个进程。进程是操作系统进行资源分配和调度的基本单位。

2. “快照”到底是什么?—— PCB (2.2.4 进程管理中的数据结构)

操作系统要同时管理几百个并发的进程,它必须有一套账本。你说的“快照”,在操作系统中被称为 PCB(进程控制块,Process Control Block)

可以把PCB想象成一个极其庞大的对象结构体,它里面记录了这个进程的所有“案底”和“生命体征”:

  • 进程标识符 (PID): 也就是进程的身份证号。

  • 处理机状态(你说的进度): 当进程被剥夺CPU时,CPU里面的各种寄存器(比如程序计数器 PC 记录着下一条要执行的代码行号、通用寄存器里算到一半的数据)必须全部原封不动地保存到 PCB 中。这就好比你在折腾 ESP32 单片机时,一旦外部硬件中断触发,系统必须立刻把当前寄存器的状态压入栈中保护起来,处理完中断再恢复。

  • 进程调度信息: 这个进程的优先级是多少?它已经等了多久了?

  • 进程控制信息: 它的代码和数据在内存的哪个物理位置?它打开了哪些文件?

铁律: PCB 是进程存在的唯一标志。创建一个进程,本质上就是为它申请一个空白的 PCB 并填入信息;杀死一个进程,本质上就是把它的 PCB 抹除。

3. 进程的状态与转换 (2.2.2 - 2.2.3 🌟 考研重中之重)

进程不是一直在一刻不停地运算的。因为它要和别的进程抢 CPU,还要等硬盘、等网络,所以它的一生都在几个状态之间不断切换。

最核心的是三个基本状态(一定要死死咬住它们的区别):

  1. 运行态 (Running): 进程不仅拥有自己需要的所有资源,而且正在 CPU 上执行代码。(在单核电脑上,任何时刻只能有一个进程处于运行态)。

  2. 就绪态 (Ready): 万事俱备,只欠东风。进程已经拥有了除 CPU 之外的所有资源,代码和数据都在内存里准备好了,只要分给它时间片,它立刻就能跑。所有的就绪进程会排成一个“就绪队列”。

  3. 阻塞态 (Blocked / Waiting): 进程在等待某个事件发生(比如等待玩家输入一段文字、等待网络下载一张图片、等待硬盘读取数据)。注意最核心的区别:在阻塞态下,即使我现在把 CPU 白送给你,你也无法运行,因为你在等的东西还没来。

举个打游戏的例子:

  • 你正在全神贯注操作角色打 Boss(运行态)。

  • 突然游戏提示“正在连接服务器…”,你在等网络数据包,这时候你什么都操作不了,CPU在游戏里也无事可做(阻塞态)。

  • 网络数据包到了,连接成功!但此时恰好系统把 CPU 时间片分给了后台正在下载文件的迅雷。你的游戏虽然可以继续玩了,但还没轮到它用 CPU(就绪态)。

进阶:“挂起 (Suspend)”到底是个什么鬼?

书上图 2-8 把 3 个状态扩展成了 5 个甚至 7 个,加入了 “静止就绪 (挂起)”“静止阻塞 (挂起)” 。为什么要把简单问题复杂化?

痛点与风险:

假设你开了 100 个网页,还开了个 3A 大作。此时内存爆满了

那些处于“阻塞态”(比如正在等网络下载)或者“就绪态”(排队等 CPU)的进程,它们的代码和数据还霸占着宝贵的物理内存,什么活也不干!这会拖死整个系统。

解决原理(这就是挂起 Suspend):

操作系统非常冷酷。它会把那些暂时用不到的进程,连同它们的 PCB 和数据,直接从内存“踢”到硬盘的交换区(Swap)里去,腾出物理内存给当前急需的进程用。

  • 被踢到硬盘上的就绪进程 静止就绪 (ReadySuspend)

  • 被踢到硬盘上的阻塞进程 静止阻塞 (BlockedSuspend)当内存空余了,或者该它运行了,操作系统再把它从硬盘读回内存,这叫激活 (Active)

✅ 真正严谨的“阻塞态”类比: 菜炒到一半,你突然发现没盐了。为了继续这道菜,你必须掏出手机点个外卖送盐。
在外卖小哥(慢速的外部 I/O 设备,如硬盘、网络)把盐送达厨房之前,你完全无法继续炒这道菜。此时这道菜只能被迫停火,盖上锅盖放在一边(进入阻塞态)。
在这个漫长的等待期里,为了不浪费时间,你转身去切另一道菜的配料(CPU 发生了上下文切换,去执行另一个就绪态的进程)。等外卖员敲门(硬件中断),这道菜才重新拥有了继续做下去的条件,回到就绪态排队。


现在,我们把目光转向剩下的内容:前趋图与并发的风险(2.1 节),以及进程通信(2.4 节)。这部分是真正考验你架构思维的地方。

1. 并发执行的客观风险:为什么要画“前趋图”?

并发看起来很美好(宏观上同时运行),但它引入了极高的系统复杂度和风险。

  • 封闭性和可再现性被破坏: 这是并发编程最让人头疼的难点。如果程序 A 和程序 B 并发执行,并且共享同一个变量 x。由于它们谁先运行、谁被中断是完全不可控的(异步性),同一个程序运行两次,可能会得到完全不同的结果!这在大型软件工程中是灾难性的。

  • 前趋图的作用: 图片 1 里的那些圆圈和箭头,就是为了解决这个风险。它是一个有向无环图(DAG),严格规定了哪些代码必须在另一些代码之前执行完成。即使是并发,也必须遵守这套严格的先后秩序,否则系统逻辑就会崩溃。

2. 进程通信 (IPC - Inter-Process Communication)

核心矛盾: 回忆第一章我们讲的“双重模式”和安全保护。操作系统为了防止流氓软件,把每个进程的内存都像堡垒一样严格隔离了。进程 A 绝对碰不到进程 B 的内存案板。
但是,现代软件不可能单打独斗。比如你在玩游戏时,一个进程负责计算物理碰撞,另一个进程负责播放音效,它们必须合作。被隔离的进程怎么交换数据?这就是 2.4 节要解决的问题。

操作系统提供了三种主要的“合法”通信渠道:

  1. 共享存储 (Shared Memory): 操作系统在内存里划出一块“公共黑板”。进程 A 把数据写在黑板上,进程 B 直接去读。

    • 特点: 速度最快,因为不需要把数据拷来拷去。

    • 风险: 极度危险。如果 A 正在写,B 同时跑去读,读到的就是错乱的半截数据。必须引入复杂的互斥锁(下一章的难点)。

  2. 管道通信 (Pipe): 相当于在进程之间接了一根“单向水管”。一个进程在水管这头疯狂倒水(写字节流),另一个在水管那头接水(读数据)。

    • 特点: 只能单向流动(半双工),自带同步机制(没水了接水的一方会自动阻塞等待)。
  3. 消息传递 (Message Passing): 进程把数据打包成一个个特定格式的“信件”,通过操作系统内核里的“邮局”发送给目标进程。

场景问题:

假设你正在构思一个对画面帧率和计算性能要求极高的系统(比如底层用 C++ 写的图形渲染引擎),其中:

  • 进程 A: 负责疯狂计算每秒几十万个多边形顶点的物理坐标变化。

  • 进程 B: 负责接收这些坐标数据,并调用显卡立刻渲染到屏幕上。

这两个独立进程每秒需要交换海量的数据。
根据讲解的原理,你会为它们选择哪种进程通信方式(共享存储、管道、还是消息传递)? 请客观说明:

  1. 为什么选这个?

  2. 在代码实现层面,使用这种方式会面临的最大风险和难度是什么?(请运用并发和隔离的直觉来回答)

✅ 正确答案:共享存储 (Shared Memory)

为了实现最高效的大数据量传输,必须选择共享存储

原理解释: 操作系统大管家会在物理内存中划出一块“公共地盘”,然后把它同时映射到进程 A 和进程 B 的虚拟地址空间里。
这就好比 A 和 B 之间原本隔着一堵墙(内存隔离),现在 OS 在墙上开了一扇窗,在窗台上放了一块共享案板。

  • 绝对的性能碾压: 进程 A 把算好的坐标直接写在共享案板上,进程 B 瞬间就能直接读到。全程不需要操作系统的内核插手,不需要来回拷贝数据,也没有用户态和核心态的切换。 它的速度基本等同于访问本地内存的速度。

⚠️ 客观风险与极高的开发难度

既然共享存储这么快,为什么不所有通信都用它?这就是你刚才提到的“时间差”问题,在专业术语里这叫竞态条件 (Race Condition)

使用共享存储最大的风险和难度在于:它没有任何自带的保护和同步机制!

  • 灾难场景: 进程 A 正在往共享内存里写入一个怪物模型的坐标。刚写完上半身,还没来得及写下半身,CPU 的时间片用完了。此时进程 B 被调度运行,它跑去共享内存一读——糟糕!它读到了一个只有上半身的新坐标和旧的下半身坐标,渲染到屏幕上就是模型被撕裂的画面(Tearing)。

  • 开发难度: 开发者必须自己写极其严谨的代码来当“交警”。比如引入互斥锁 (Mutex)信号量 (Semaphore)。A 在写的时候必须上锁,B 即使想读也必须在外面等(进入阻塞态),等 A 写完解锁了,B 才能进去读。一旦锁的逻辑写错一点,就会导致整个系统死锁(Deadlock),两个进程永远互相等待,游戏直接画面冻结。


关于线程

1. 为什么要有线程?(被逼出来的痛点)

前面学了,进程很好,它通过内存隔离把系统保护得严严实实。但这种“重装甲”也带来了极大的性能开销

想象正在进入一个包含很多效果的网页:

  1. 监听并响应用户的鼠标滑动和点击。

  2. 渲染那些极其丝滑的 CSS 粒子动画。

  3. 在后台通过网络请求拉取最新的数据。

如果按照第一章的“并发”思路,把这三件事变成三个独立的进程会怎样?

  • 切换太慢: 每次从“画动画”切换到“响应鼠标”,操作系统都要保存上一个进程庞大的 PCB,还要切换整个虚拟内存的映射表。这就像让一个重装步兵频繁换装一样,极其消耗 CPU 资源。

  • 通信太难: 动画进程想要根据网络进程拉取的数据来改变形态,它们俩被厚厚的“内存墙”隔开,必须求助操作系统大管家,用我们刚才说的“共享存储”或“管道”来通信,开发极其繁琐。

为了解决这个问题,计算机科学家在进程的内部,又切分出了更细的执行单位——线程

2. 掰开揉碎:进程 vs. 线程 的本质区别

这是所有大厂面试和考研专业课必考中的必考,你只需要记住这个核心比喻:

  • 进程(Process):是操作系统分配资源的基本单位。 它就像是一个封闭的车间(拥有独立的内存空间、打开的文件、各种硬件资源)。

  • 线程(Thread):是 CPU 调度和执行的基本单位。 它就像是车间里的流水线工人

关键特征与原理:

  1. 资源归属: 线程自己几乎不拥有什么系统资源(除了一点点必不可少的“私房钱”,也就是存它自己执行进度的 TCB(线程控制块)和少量的寄存器/栈空间)。它吃穿用度全靠它所在的那个进程。

  2. 天然共享: 同一个进程下的所有线程,完全共享这个进程的所有内存和资源!它们同在一个车间,抬头不见低头见,线程 A 算出来的变量结果,线程 B 可以直接从内存里拿来用,完全不需要操作系统内核插手通信! 这速度极快。

  3. 轻装上阵: 因为线程共享内存,所以在同一个进程内切换两个线程时,操作系统不需要切换内存映射表,只需要换一下几个寄存器的值。切换速度比切换进程快了几个数量级。

3. 客观存在的极高风险与难度

不要以为线程全是优点。在实际的代码工程中,多线程编程是无数程序员的噩梦。

  • 风险一:极度脆弱(一损俱损) 因为进程里的线程互相不设防(没有隔离),如果其中一个负责拉取网络数据的线程,因为写了段烂代码导致了“内存访问越界”(比如试图去读不属于自己的内存地址),操作系统会立刻捕捉到这个严重异常。 后果是毁灭性的: 操作系统才不管你这个进程里还有几个正常工作的线程,它会直接把整个进程强制连根拔起杀死!你的整个网页或游戏会瞬间白屏闪退。

  • 风险二:并发冲突(踩踏事件) 既然大家共享同一个内存案板,如果线程 A 正在往变量 x 里写数据,线程 B 同时跑去读 x,或者它们俩同时对 x 进行加一操作,数据就会彻底错乱。这就是多线程开发中最难啃的骨头:线程同步与互斥锁。你必须自己在代码里写极其严谨的“交通规则”,否则系统跑起来就是一个随机崩溃的定时炸弹。


    第 3 章 处理机调度

    这一章是整本操作系统的算法核心。如果说第一章是告诉你操作系统是“大管家”,第二章告诉你大管家手底下管着很多“进程(员工)”,那么第三章就是要解决一个极其残酷的现实问题:CPU 只有一个(或者少数几个),但嗷嗷待哺的进程有几百个,大管家到底该把 CPU 分给谁?先给谁?后给谁?给多久?

    这就是调度。我们掰开揉碎来看 3.1 和 3.2 节的核心。

    1. 调度的三个层次 (3.1.1 节 - 🌟 必须分清的宏观概念)

    操作系统调度不是一刀切的,它分为三个级别,就像医院看病一样:

    • ① 高级调度(作业调度):从硬盘 $\rightarrow$ 内存

      • 原理: 硬盘里有一堆程序排队等着运行。高级调度就是决定把哪些程序从硬盘调入内存,给它们创建 PCB,变成“就绪态”。

      • 比喻: 医院大门外的保安,决定今天放哪些病人进医院大厅排队。发生频率最低(几分钟一次)。

    • ② 中级调度(内存调度):内存 $\leftrightarrow$ 硬盘(挂起区 Swap)

      • 原理: 这个你已经学过了!就是第二章里的挂起 (Suspend)。内存不够了,把暂时不跑的进程踢到硬盘上;内存空了,再把它们接回来。

      • 比喻: 医院大厅人太多了,护士把一些病情不急的病人先赶到院子外的长椅上等,大厅空了再叫进来。发生频率中等

    • ③ 低级调度(进程调度):内存(就绪队列) $\rightarrow$ CPU

      • 原理: 这是整个操作系统最核心、最频繁的动作! 决定从就绪队列中挑哪一个进程,把 CPU 的控制权交给它。

      • 比喻: 诊室里的医生,喊门外排队的人:“下一个,张三!”。发生频率极高(每秒钟成百上千次)。

    ⚠️ 风险提示: 考试或面试时,不要把这三个搞混。我们这章后续讨论的各种牛逼算法,绝大多数都是指“低级调度(进程调度)”

    2. 调度算法:天下没有免费的午餐 (3.2 节)

    怎么挑下一个进程?这就是书上 3.2 节列出的一大堆算法。每一个算法都是为了解决上一个算法的痛点而发明的,但同时又会引入新的风险。我挑最核心的三个给你建立直觉:

    ① 先来先服务 (FCFS - First Come First Serve)

    • 原理: 谁先排队,谁就先用 CPU,用到死或者用到自己阻塞为止。

    • 致命风险: “饥饿”现象(护航效应)。假设一个极其耗时的视频渲染进程(需要 10 小时)排在第一,后面跟着 100 个只需要 1 毫秒就能完成的打字响应进程。那这 100 个轻量级进程会被完全卡死。系统会显得极度卡顿。

    ② 短作业优先 (SJF - Shortest Job First)

    • 原理: 预测哪个进程需要的运行时间最短,就先给谁安排 CPU。

    • 优势: 数学上可以证明,这种算法的平均等待时间是最短的

    • 致命风险: 长作业饿死。如果系统里源源不断地涌入短任务,那个需要算 10 个小时的视频渲染进程可能永远也分不到 CPU,直接死在就绪队列里。而且,操作系统其实根本无法准确预测一个进程到底要跑多久。

    ③ 时间片轮转 (RR - Round Robin) —— 现代操作系统的基石!

    • 原理: 绝对的公平。大家排成一个环。系统给每个人发一个固定大小的“时间片”(比如 10 毫秒)。时间一到,不管你有没有算完,强制剥夺 CPU(通过硬件定时器中断),把你踢到队伍末尾重新排队。

    • 优势: 解决了 FCFS 的护航效应,也解决了 SJF 的饿死问题。用户的交互体验极好(你一边打游戏一边听歌不卡,就是因为这个)。

    • 致命风险: 上下文切换开销(Context Switch Overhead)。如果时间片设得太短(比如 0.1 毫秒),CPU 一天到晚都在忙着保存 PCB、恢复 PCB,真正用来计算业务代码的时间反而变少了,系统总效率断崖式下跌。


      拆解:优先级调度算法 (Priority Scheduling)

      顾名思义,就是给每个进程发一个“VIP 等级”。调度的时候,永远挑 VIP 等级最高的那个先执行。

      核心考点与风险分析:

      1. 抢占式优先级 vs 非抢占式优先级:

        • 假设正在运行一个 VIP 3 的进程。此时就绪队列里突然新来了一个 VIP 5 的进程。

        • 如果是非抢占式:VIP 5 只能乖乖排队,等 VIP 3 运行完。

        • 如果是抢占式:操作系统立刻触发中断,把 VIP 3 一脚踢开(保存 PCB),直接让 VIP 5 上 CPU。在对实时性要求极高的系统(比如导弹拦截系统、自动驾驶汽车)中,必须使用抢占式优先级。

      2. 致命的系统风险:饥饿 (Starvation) 这是优先级调度算法最大的痛点。如果系统里源源不断地涌入高优先级的进程(比如 VIP 4, VIP 5 不断产生),那一个可怜的 VIP 1 进程可能会在就绪队列里永远等不到 CPU,直到宇宙毁灭。这在学术上叫“饥饿”。

        如何解决饥饿?—— 老化机制 (Aging) 操作系统的设计者极其聪明。为了防止低优先级进程被饿死,他们引入了“时间”这个维度。一个进程在就绪队列里等待的时间越长,操作系统就会逐渐提高它的优先级。 比如那个 VIP 1 等了 10 分钟,系统把它提拔成 VIP 2;等了 1 个小时,提拔成 VIP 5。这样,只要等得足够久,哪怕是最底层的进程,最终也能变成最高优先级,从而拿到 CPU 运行。

        现代操作系统(比如 Linux、Windows)根本不用单一的 FCFS 或 RR,而是把它们全部缝合起来。
        它的核心原理是设置多个就绪队列

        1. 队列 1(最高优先级,时间片极短,比如 10ms): 所有新来的任务(不管是 A 还是 B)都先进入队列 1。

          • 效果: B 这种 5 秒就能跑完的短脚本,在头几个极短的时间片里就能迅速执行完毕,实现了极低的响应延迟
        2. 队列 2(中等优先级,时间片较长,比如 50ms): 如果 A 任务在队列 1 的 10ms 内没跑完(显然跑不完,它要 3 天),操作系统大管家就会识破它是个“耗时大户”,无情地把它降级,踢到队列 2。

        3. 队列 N(最低优先级,采用 FCFS 或极长的时间片): 像 A 这种连续好几次跑不完的任务,最终会掉到底层队列。当上面的短任务都处理完时,系统就把大段的 CPU 时间全给 A 慢慢算,保证了高吞吐量

        这就是混合的艺术:既保证了短任务秒回,又保证了长任务最终能算完,而且不需要提前知道任务到底要跑多久。