ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

虚拟存储器深度解析:请求分页、缺页中断与页面置换算法

虚拟存储器深度解析:请求分页、缺页中断与页面置换算法 如果你正在啃《计算机操作系统》大概率会在第五章“虚拟存储器”这里卡一下。前四章讲连续分配、分页、分段这些还都能在脑子里画出图来可一到虚拟存储器请求分页、缺页中断、页面置换、抖动、工作集……术语一个接一个每个词单看都认识拼在一起就不知道系统到底在干什么。我当年学的时候也懵后来带过几轮操作系统课程又对照 Linux 真实的内存管理机制回头啃了一遍才把这一章的脉络理顺。这篇文章就把我认为最重要的主干、最容易踩的认知误区、以及考试和实际工程里都要用的思考方式从头到尾梳理一遍。不管你是期末速成、考研复习还是工作后想补操作系统短板都值得花二十分钟过一遍。主流教材里汤小丹老师的《计算机操作系统》和配套的慕课版课程这一章的框架基本一致可以直接对接。1. 先搞懂“虚拟存储器”到底在解决什么问题1.1 内存不够用不是只有一种解法很多同学学这一章时有个误区以为虚拟存储器就是“内存不够就放到磁盘上”。这个说法太粗糙了。在虚拟存储出现之前系统解决大程序装不进内存的问题靠的是覆盖和对换两条路。覆盖技术是把程序拆成多个覆盖块程序员需要手工规划哪些模块可以共享同一个内存区、什么时候加载哪一块。这非常依赖人对程序执行顺序的把控写起来像是在做手工拼图维护成本极高而且对现代这种依赖动态库、运行时才加载模块的程序来说根本不现实。对换技术则是以整个进程为单位把内存里暂时不运行的作业换到磁盘上的对换区再让新作业进来。它的粒度太大进程刚换进来可能只跑了一小会儿又被换出去频繁的整段换入换出会让系统开销高得离谱。虚拟存储器的思路完全不同一个程序运行的时候并不需要把全部指令和数据都放在内存里。程序在一个时间段内真正频繁访问的往往是很小的一部分代码和数据。所以系统只需要把“马上要用的部分”装进内存其余留在磁盘上当访问到不在内存的内容时再按需调入。用户视角下程序认为自己拥有一整块连续的大地址空间哪怕这个空间远远大于物理内存。这就是“虚拟”二字的来源。这个思路可以类比图书馆自习你一个学期要读几十本参考书但桌上只需要放今天要用的几本。书架上的书就是外存书桌就是内存偶尔去书架换书就是调页和置换。传统做法相当于要求你开学第一天就把所有书全部堆到桌子上——显然不现实。1.2 三个基本特征多次性、对换性、虚拟性这一章几乎所有机制都是围绕虚拟存储器的三个基本特征展开的。多次性作业不必一次性全部装入内存允许分多次调入。这是虚拟存储与传统对换的本质区别传统对换要求整个作业先装进内存。对换性进程运行过程中允许把暂时不用的页面换出到外存需要时再换回。没有对换性内存里的空间一旦被占满按需调入就成了空谈。虚拟性逻辑上扩充了内存容量用户看到的逻辑地址空间可以远大于物理内存。多次性和对换性是实现虚拟性的手段虚拟性是最终目标。考试如果问“虚拟存储器的主要特征”这三个词就是标准答案而且顺序最好不要乱多次性是对换性的基础对换性让虚拟性成为可能。1.3 为什么局部性原理是整章的“地基”虚拟存储能成立不是靠操作系统自己硬扛而是靠程序运行时的一个客观规律——局部性原理。局部性分成两种时间局部性指刚访问过的数据/指令很可能很快再次被访问典型例子就是循环体、计数器、栈顶元素空间局部性指访问了一个地址后附近的地址很可能马上被访问典型例子是顺序执行的指令、数组的连续遍历。你可以设想一下如果程序的访问模式是完全随机的每次访问的页面都不重复那么按需调入策略就会退化成“每访问一个页面就去磁盘读一次”系统性能会崩得一塌糊涂。正因为局部性存在我们才敢用少量物理内存去承载大得多的虚拟地址空间并且让平均性能维持在一个可接受的水平。所以局部性原理不是理论推导出来的而是大量程序运行行为的经验规律是虚拟存储器一切的合法性来源。2. 请求分页虚拟存储器在分页系统上的落地2.1 页表被扩充成了什么样前面学的基本分页管理要求进程的所有页一次性装入内存这显然阻碍了虚拟思想的落地。请求分页 基本分页 请求调页 页面置换。也就是说进程的页面可以先只装入需要的几页运行过程中缺哪页就调入哪页内存满了再考虑换出哪些页。要做到这一点原来的页表项必须扩充字段因为操作系统需要知道每一个页的更多状态。页表项字段含义为什么需要状态位P该页是否已在内存访问时先查它不在内存就去申请缺页中断访问字段A记录该页最近被访问的情况供页面置换算法决定淘汰谁修改位M该页是否被修改过换出时如果修改过必须写回磁盘没修改可以直接丢弃外存地址该页在磁盘上的存放位置缺页时必须知道去磁盘哪里读取这里特别提一下修改位很多初学者会忽略它的价值。一个没有被修改过的页换出时不需要写回磁盘因为磁盘上本来就有一份原样的副本直接把页框回收就行被修改过的页则必须先写回磁盘否则数据就丢了。写回一次磁盘的开销远大于在内存里改几个标志位所以优秀的置换算法都会优先淘汰“没被修改过”的页这就是后面改进型 Clock 算法的出发点。2.2 缺页中断整个机制的核心引擎请求分页和基本分页最核心的区别就是多了一个缺页中断机制。缺页中断的处理流程是这一章必考的过程题要能按顺序默写CPU 访问逻辑地址MMU 查页表时发现该页状态位为 0说明页面不在内存。硬件触发缺页中断进程从用户态陷入内核态。操作系统检查该页在内存中的合法性然后在外存地址字段找到磁盘上的位置。检查内存中是否有空闲页框。没有的话依据页面置换算法选择一个牺牲页如果牺牲页被修改过先写回磁盘。将所需页面从外存调入内存更新页表的状态位、访问位、内存页框号等。恢复进程执行重新执行刚才那条被中断的指令。缺页中断和普通 IO 中断有两个很大的区别考试特别爱出辨析题。第一普通中断一般是在一条指令执行完之后才响应而缺页中断是在指令执行期间产生的因为访问内存是指令执行过程中的一部分。第二普通中断处理完成后通常回到断点继续执行下一条指令但缺页中断处理完成后必须回到那条引发缺页的指令重新执行。更离谱的是一条指令可能产生多次缺页中断典型的例子是串操作指令或带间接寻址的指令一次访问的数据跨了好几个页面缺一个页处理完重新执行时又发现下一个页不在那就得再缺一次。2.3 一次地址变换的完整流程这一章计算题的基础是能熟练描述“从逻辑地址到物理地址”的完整过程。我以分页系统为例拆开讲。假设页面大小为 1KB页表已在内存中。CPU 给出逻辑地址 4099先拆出页号 P 4099 / 1024 4页内偏移 w 4099 % 1024 3。接着按下面几步走先检查页号 4 是否大于页表长度如果超出范围说明是越界访问触发越界中断。查快表 TLB看页号 4 对应的页表项是否已经在快表里。如果 TLB 命中直接得到页框号。假设页框号是 10物理地址 10 × 1024 3 10243访问结束。如果 TLB 未命中就去内存查页表。查到页表项的状态位是 1说明页面在内存把页表项重新写入快表快表满就淘汰一项然后组装物理地址。如果状态位是 0走缺页中断处理流程处理完回来重新访问。这个过程中有一个很容易被忽略的代价没有快表时每次访问内存数据之前要先访问一次页表等于一次逻辑访问要付出两次内存访问的代价。加了快表之后如果 TLB 命中率高这个额外开销就被压得很低。考察有效访问时间 EAT 的计算题很常见题眼在于分清“命中”和“未命中”的路径。例如 TLB 命中率 98%TLB 访问时间 20ns内存访问时间 100ns则 EAT 0.98 × (20 100) 0.02 × (20 100 100) 122ns。而如果没有快表EAT 固定是 200ns。一个 98% 的命中率就把平均开销从 200ns 降到 122ns这还是在题目给定的参数下真实系统里 TLB 命中率通常超过 99%快表的重要性不言而喻。2.4 进程“假装”拥有大内存虚拟地址空间的视角这里插一个很多初学时会困惑的点既然物理内存可能只有 4GB进程却能申请 8GB 的虚拟内存这不矛盾吗其实不矛盾因为虚拟地址空间和物理页框之间是多对一的松散映射关系。进程申请虚拟内存时操作系统只是把虚拟地址空间中的一段标记为“已分配”并不立刻分配物理页。真正读写这些地址时才触发缺页物理页才被“按需”挂上去。这也解释了为什么在很多监控工具里进程的 VSZ虚拟内存大小远远大于 RSS实际物理内存占用——正常情况下这不是内存泄漏而是虚拟存储的正常表现。3. 页面置换算法从理论最优到工程妥协3.1 评价尺子OPT 最优置换算法缺页一旦发生如果内存没有空闲页框就必须决定换出哪一页。这个决策直接决定缺页率所以页面置换算法是整章的重头戏。最优置换算法 OPT 的思路极其简单粗暴选择“将来最长时间不会被访问”或者“以后再也不会用到”的页面换出。从理论上看这样产生的缺页次数一定是最少的所以它被当作一把尺子用来衡量其他算法的上限。但 OPT 有一个致命问题系统不可能预知未来访问序列。所以它只能作为理论基准存在在实际系统里根本没法实现。考试的时候给你一个访问序列你要能按 OPT 规则手算出缺页次数这主要是训练“向后看”的思维方式。我习惯用这个经典访问序列来对比各种算法7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3, 2, 1, 2, 0, 1, 7, 0, 1。在 3 个页框下OPT 的缺页次数是 9 次。第一次装载 7、0、1 三次缺页第 4 次访问 2 时因为 7 下一次出现的位置最远要等到第 18 次所以淘汰 7后面依次按类似逻辑决策。这个例子后面会继续用作对比。3.2 FIFO 的简单与 Belady 异常先进先出算法 FIFO 最好理解哪个页面最早进入内存就先淘汰谁一个队列就搞定了。实现代价几乎为零但它完全不尊重程序的局部性规律一个被反复使用但进入内存较早的页面可能会被反复换出再换入效率一塌糊涂。FIFO 最著名的“翻车现场”是 Belady 异常给进程增加页框数缺页次数反而增加了。这在直觉上非常反常识因为大多数人觉得“内存给得越多表现应该越好”。用访问序列 1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5 来验证3 个页框时缺页 9 次4 个页框时缺页反而变成 10 次。你多给了一个物理页框系统缺页次数却更多了。这里的关键是 FIFO 不是栈式算法它的淘汰集合不是固定嵌套的因此可能出现这种异常。这也是考试特别喜欢拿出来做计算题的考点基本每个老师都会强调一遍。3.3 LRU理论上更好硬件开销也更高LRU最近最久未使用算法认为如果一个页面最近这段时间一直没被使用那么在最近的将来被使用的概率也很低应该优先淘汰它。它符合局部性原理而且 LRU 属于栈式算法不会出现 Belady 异常。问题是 LRU 的实现成本很高。要在每次访问时记录页面的访问时间或者维护一个按访问时间排序的页面栈硬件上需要大量寄存器或专用电路成本非常高纯软件实现又需要在每次访存时做额外的时间戳比较和调整操作程序的执行速度会被拖累。所以在真实系统里很少实现严格的 LRU而是采用 LRU 的近似算法。最典型的是 Clock 算法也叫 NRUNot Recently Used算法。每个页框维护一个访问位当页面被访问时置 1。需要淘汰页面时指针循环扫描遇到访问位为 0 的页面就换出遇到访问位为 1 的就把它清 0继续扫描。可以想象成指针在内存里的页框之间“转圈圈”转一圈总能找到一个可以淘汰的页面所以叫 Clock。如果所有页面的访问位都是 1Clock 退化为 FIFO——指针转一圈把所有访问位都清了相当于按进入顺序淘汰。这个退化情况要能意识到考试喜欢问“Clock 在什么情况下接近 FIFO”。3.4 改进型 Clock把修改位也用上严格 LRU 成本太高朴素 Clock 又完全没有考虑“页面是否被修改过”这一重要信息。于是有了改进型 Clock给每个页框维护访问位和修改位组合成四种状态状态含义换出代价(0,0)未访问、未修改最低直接丢弃回收(0,1)未访问、已修改需要写回磁盘(1,0)已访问、未修改干净但最近被用过(1,1)已访问、已修改最“舍不得”又要写回又要访问过改进型 Clock 的淘汰顺序是优先找 (0,0)找不到再找 (0,1)前两轮都不动手改访问位第三轮才把访问位清零重新找 (0,0)最后一轮找 (0,1)。说白了宁可让算法扫描好几圈也要先避开“脏”页面因为写回磁盘的代价比在内存里多转几圈大得多。这个思路即使是到了 Linux 这类现代系统中也依然能在各种近似置换策略里看到影子。3.5 做题技巧手算置换算法怎么不写错我见过太多同学手算缺页次数时出错几乎都栽在同一个细节上没有把“首次装入也计为缺页”这件事在草稿纸上标清楚。缺页次数和置换次数是两个概念第一次装入某个页面时虽然发生了缺页但不算置换。题目可能只问缺页次数也可能只问置换次数看题要仔细。手算 LRU 时我强烈建议用栈的方式模拟访问某页时如果它已在栈中就把它提到栈顶如果不在就压入栈顶并淘汰栈底。这样每步都有明确状态不容易乱。手算 Clock 时则一定要画出指针位置每次扫描都标出当前指针走到哪了否则多转几圈自己就绕晕了。回到前面的经典序列 7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3, 2, 1, 2, 0, 1, 7, 0, 1在 3 个页框下OPT 是 9 次LRU 是 12 次FIFO 是 15 次。这就是三种算法性能差距的直观体现也是考研和期末最常考的对比素材。算法缺页次数3页框是否可实现会不会Belady异常工程成本OPT9否理论基准不会理论最优无法实现LRU12是不会高FIFO15是会极低Clock/NRU视实现约13-15是近似LRU通常不会低4. 页面分配策略与抖动为什么系统会突然“卡死”4.1 每个进程分多少页框是个策略问题页面置换算法解决的是“该淘汰谁”但还有一个前置问题每个进程应该拥有多少物理页框这个数量叫驻留集大小。驻留集太小进程频繁缺页系统把大量时间浪费在磁盘调页上驻留集太大内存被白白占着别的进程不够用。分配策略常见的有三种平均分配把系统物理页框平均分给所有进程。听起来公平但 20 个页框的程序和 200 个页框的程序分得一样多显然不公平小进程浪费、大进程缺页。按比例分配根据进程的地址空间大小按比例分页框比平均稍合理但仍然没有考虑进程访问密度的差异。按优先权分配给高优先级作业多分页框。这是主流操作系统调度的默认倾向——你越重要我给你的资源越多。更关键的是分配策略和置换范围的组合。固定分配 局部置换是最保守的每个进程的页框数启动时定死缺页了只能从自己的页框里换可变分配 全局置换是最灵活的系统维护一个空闲页框池谁缺页谁可以从池子里拿还可以抢占别的进程的空闲页框所以全局置换容易让一个“胃口大”的进程把别人的页框都抢走可变分配 局部置换则是折中既允许进程缺页时从池子里申请页框也允许系统定期重新评估回收或增加页框数。考试喜欢出一个表让填“哪种策略最公平”“哪种策略系统开销最小”复习时可以把上面三种组合列出来对照记忆。4.2 抖动表面忙碌实际瘫痪抖动颠簸是虚拟存储中最危险的现象。当一个进程频繁缺页并且系统频繁地调入调出页面调页时间已经超过了进程实际运行的时间时系统就进入了一种假死状态。最诡异的特征是CPU 利用率不仅没有升高反而急剧降低但磁盘 IO 始终处于满负荷运转系统看起来“很忙”实际没有任何有效工作完成。抖动的恶性循环是这样的进程缺页率高说明分配给它的页框太少于是系统尝试给进程增加页框但物理内存总量有限增加页框就要从别的进程那里抢被抢的进程也陷入缺页风暴于是系统只能通过置换算法疯狂换页换页本身要占用 CPU 和磁盘 IO真正可用于进程执行的 CPU 时间越来越少CPU 利用率下降系统发现 CPU 太闲以为负载不够又引入更多新进程进一步加剧内存紧张。最终整个系统陷入瘫痪。这里有一个反常识的判断题经常考“CPU 利用率低说明系统负载低、很空闲。”在抖动的场景下这是错的。抖动时 CPU 利用率低是因为进程大部分时间卡在缺页中断里等待磁盘 IO而不是真的没有工作可做。判断系统是否抖动的关键指标之一就是磁盘调页 IO 是否异常高。4.3 工作集模型用窗口看程序的“胃口”工作集模型是 Denning 提出的对付抖动的理论武器。它的定义是在一段时间窗口 Δ 内进程实际访问过的页面集合称为工作集 W(t, Δ)。这里的 Δ 不是时间长度而是“最近 Δ 次内存访问”这个窗口。工作集概念的洞见在于程序的局部性会随时间变化。一个程序在生命周期里会经历不同的阶段每个阶段密集访问的页面集合不同。在阶段 A工作集可能是 {a,b,c,d}进入阶段 B工作集可能变成 {e,f,g}。如果你只看驻留集总量忽略工作集内容的变化就会在阶段切换时出现大量缺页。防止抖动的核心思想是每个进程的驻留集大小应当不小于它的工作集大小。如果驻留集小于工作集哪怕只是少了一页都会导致缺页率直线上升因为那一页正是当前热点。操作系统还可以通过周期性统计每个进程的工作集动态调整驻留集把多余页框匀给缺页严重的进程。除了工作集模型还有一些工程上的辅助手段挂起部分进程把它们的页框全部释放出来给活跃进程或者采用 LS 准则让产生缺页的平均时间大致等于换取页面传输的平均时间让调页系统处于均衡状态。这些手段的核心目的都一样让每个进程的物理页框数量维持在一个“够用但别浪费”的区间。5. 请求分段与段页式另一条通往虚拟存储的路5.1 请求分段按逻辑切按需调入分页系统是按固定大小切页优点是内存管理简单、碎片少缺点是完全不考虑程序逻辑。一个函数可能跨好几个页面一次函数调用可能触发多次缺页而且一个运行中的程序想在多个进程间共享某段代码时分页系统的处理相当笨拙。请求分段则不同段是程序的逻辑单位代码段、数据段、栈段、共享库段。每个段大小不一按需调入调出。段表项除了一般的段基址和段长同样需要状态位、访问位、修改位、外存地址还有一项“增补位”用来标记段是否允许动态增长比如栈段的长度会随函数调用层次加深而变化。缺段中断的处理流程和缺页类似但更麻烦段大小不固定内存分配时要处理外部碎片可能要先做紧凑或者对换腾出一整块连续空间才能装下这个段。段太长时一次调段的开销也远高于调一页。请求分段最大的优势是逻辑独立性。多个进程可以共享同一个代码段因为共享单位是完整的一段程序逻辑而不是散乱的页面动态链接也容易实现因为加载一个库就是加载一个整段。分段还天然支持保护每个段可以设置读写执行权限代码段不可写、数据段不可执行这对系统安全意义重大。5.2 段页式逻辑清晰和内存高效的组合拳分页和分段各有优势段页式想两个都要先按程序的逻辑分段再在每个段内分页。逻辑地址被拆成三段段号 段内页号 页内偏移。一次完整的地址变换要查两级表。第一级查段表检查段号是否越界定位到该段的页表第二级查页表检查页号是否越界、状态位是否为 1定位到物理页框。再加上访问目标内存一共三次访存。没有快表的话这开销比纯分页还要多一次所以段页式系统对快表的要求更高通常需要在快表里同时缓存“段号 页号 → 页框号”的映射。虚拟存储下的段页式更复杂一点缺页时如果该段对应的页表本身不在内存里还得先触发一次缺页中断把页表装入内存再去处理真正缺的页面。两个中断叠在一起处理逻辑相当绕。考试一般不深究到这一步但你要能说出“段页式访问一次数据至少三次访存”这个结论以及快表优化后的效果。6. 回到真实系统Linux 怎么实践这一章的思想6.1 Linux 进程的虚拟地址空间长什么样理论说再多不如在真实系统里看一眼。以 32 位 Linux 为例每个进程拥有 4GB 虚拟地址空间其中用户空间占 0~3GB内核空间占 3~4GB。进程眼里自己有整整 3GB 的“私人领地”哪怕这台机器的物理内存只有 512MB程序照样能申请接近 3GB 的虚拟地址空间只是真正使用多少物理内存取决于运行时的实际访问情况。进程虚拟地址空间从低到高大致是代码段、已初始化数据段、未初始化数据段、堆、mmap 映射区、栈、内核映射区。其中堆向上增长栈向下增长中间还有 mmap 区域这些区域并不是一开始就分配好物理页的。在 Linux 上可以用pmap pid查看每个进程的虚拟内存区域分布也可以用cat /proc/pid/maps看到每一段虚拟地址区间的权限和映射关系。我第一次在ps输出里看到某个进程的 VSZ 超过 2GB 而 RSS 只有几十 MB 时才真正理解了“虚拟”这两个字的含义。6.2 缺页异常和写时复制现代操作系统的日常Linux 进程访问一个虚拟地址时如果对应的物理页还没建立映射CPU 会触发缺页异常。以 x86 为例入口是do_page_fault一路往下会到handle_mm_fault。内核会检查这个虚拟地址是否落在某个合法的虚拟内存区域VMA由vm_area_struct描述里如果不在任何 VMA 中就发送 SIGSEGV 信号进程直接段错误崩溃。这就是 C 语言里常见的 Segmentation fault 的底层逻辑——不是某个数值算错了而是访问的虚拟地址连合法的映射区域都不存在。缺页处理还要区分几种触发场景。匿名页缺页malloc 之后第一次写这块内存此时还没有物理页内核分配一个零页给进程文件映射缺页mmap 映射的文件内容从未读入内存缺页时从磁盘读文件页写时复制缺页这是 fork 系统调用的经典优化。写时复制是虚拟存储思想在进程管理里的得意之作。fork 一个子进程时内核不为父子进程复制全部物理页而是让它们共享同一批物理页并把这些页全部设为只读。无论父进程还是子进程只要一方执行写操作就会触发写保护缺页内核这才复制那个物理页再把权限改回可读写。正是这种“先共享、写时再复制”的机制让 fork 一个大型进程变得极快也让虚拟内存在现代操作系统里的作用从“解决内存不够”升级成了“大幅提升系统性能的手段”。6.3 从 C 语言视角理解虚拟存储器学习这一章的时候如果会一点 C 语言你会看到很多非常直观的现象。比如#include stdio.h #include stdlib.h #include string.h int main() { // 申请2GB虚拟内存 size_t size 2UL * 1024 * 1024 * 1024; char *p (char *)malloc(size); if (!p) { printf(malloc failed\n); return 1; } // 到这里p指向2GB虚拟地址但物理内存几乎没有额外占用 printf(before memset, press enter\n); getchar(); // 第一次写入触发缺页逐页分配物理内存 memset(p, 0, size); printf(after memset, press enter\n); getchar(); free(p); return 0; }在 Linux 上运行这段程序分别在两次getchar()时用ps -o pid,vsz,rss,comm -p pid观察你会看到 VSZ 一直很大但第一次输出后 RSS 很小第二次输出后 RSS 才接近实际占用的物理内存量。这就是“按需调页”最直接的可视化实验。类似的如果你 malloc 后立刻 free但指针还继续使用行为是未定义的——有时候能读出旧数据有时候直接段错误这种运气成分正是虚拟存储和页权限带来的不确定性。6.4 这一章怎么考、怎么学期末和考研对这一章的考查非常集中核心题型就那几类地址变换计算题给你逻辑地址、页面大小、页表求物理地址置换算法计算题给你访问序列分别用 OPT、FIFO、LRU 求缺页次数快表有效访问时间计算然后是围绕抖动、工作集、Belady 异常的问答题。这些题型的套路性很强熟练之后分数很好拿。学习路径上我建议看慕课版视频时用两倍速建立概念框架但遇到计算题必须暂停自己在纸上推一遍。推完之后去 Linux 上跑一跑上面那个 C 程序再看看/proc/pid/maps你会发现这一章的知识不再是纸上谈兵。很多人学到后面把虚拟存储器当成纯粹为了考试而背的概念等到真正排查内存问题、优化性能时才追悔莫及回头再补这一章的成本就高多了。我个人带学生时最深的体会是这一章是这个学科的“分水岭”。前四章是搬砖的体力活第五章之后才开始真正考验一个学习者的抽象能力和系统思维。把虚拟存储器学透了后面文件系统、设备管理、甚至分布式系统的很多概念都会变得顺理成章。如果现在读到这里有某一段还卡着建议先别往下赶回到对应小节重新推一遍那个 7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3, 2, 1, 2, 0, 1, 7, 0, 1 的例子把三种算法的每一步行都写出来比盯着课本看十遍都管用。
返回列表