你有没有想过,一个程序从双击图标到窗口弹出,在这期间操作系统究竟在内存里做了哪些事?很多人学操作系统,一上来就被进程、线程、死锁这些概念劝退,其实这个领域最核心的套路,从三四十年前就已经定下来了。
这篇文章是电子科技大学《操作系统》网课的笔记整理,对应的是存储管理部分的开篇内容——“简单存储管理”。也就是现代内存管理的“史前史”,讲的是在没有分页、没有分段、没有虚拟内存的年代,操作系统是怎么把程序装进内存跑起来的。学完这一块,你再去看后面那些复杂机制,会发现它们全都是为了解决这里暴露出的问题而设计的,思路一下就通了。
这篇笔记适合三类人:正在上操作系统课、被各种分配算法绕晕的学生;期末复习想要快速梳理存储管理脉络的考生;以及自学计算机基础、想弄明白“内存地址到底怎么来的”的爱好者。我会尽量用大白话和实际案例来讲,保证每一个概念都能落地,不看天书。
1. 程序跑起来之前,内存里发生了什么
很多教材一上来就扔给你一堆术语:逻辑地址、物理地址、重定位、碎片……但少有人先回答一个最基础的问题:为什么操作系统要专门搞一套存储管理机制?程序直接读取内存不行吗?
1.1 没有存储管理器的年代:一个程序“独占”内存的奢侈日子
在最原始的计算机上,情况确实就是“直接读”。那时候没有多道程序的概念,一次只跑一个任务。程序员把程序加载到内存的固定位置,比如从地址0开始,然后CPU按顺序取指令执行。整个过程里,内存就是程序的地盘,谁也不用跟谁抢。
这个阶段最大的问题不是“浪费”,而是“脆弱”。程序在运行中如果发生了逻辑错误,比如数组越界、野指针操作,它可能直接写入任意一个内存地址。写到哪里算哪里,轻则程序崩溃,重则把操作系统的代码也给覆盖掉,整个机器死机。早期计算机的调试难度之所以那么高,很大一部分原因就是这个——程序对内存拥有绝对的访问权,出了错你根本定位不到是哪一句指令干的好事。
更要命的是,这种模式完全无法支持多道程序。你想想,如果内存里同时驻留两个程序,程序A写坏一个地址,程序B可能就莫名其妙崩了。两个程序之间没有任何隔离,大家都裸奔。而后来计算机的发展方向恰恰是多道批处理、分时系统,需要多个程序同时待在内存里,轮流使用CPU。一旦要“同时共存”,“地盘划分”和“互不侵犯”就成了刚需。
1.2 存储管理要干的事情,其实就四件
所以存储管理机制不是设计者闲得慌,而是被现实逼出来的。归纳一下,任何存储管理系统都要回答四个问题:
- 内存空间怎么分:多个程序同时驻留,每个程序占哪块区域?这是空间分配问题。
- 内存不够怎么办:程序需要的空间大于物理内存怎么办?这是空间扩充问题。
- 物理地址怎么定:程序里写的地址和实际内存地址对不上,谁来翻译?这是地址转换问题。
- 程序之间怎么隔离:怎么防止程序A访问程序B的内存?这是保护问题。
电子科大网课里把“简单存储管理”当成一个整体来介绍,但我学下来觉得,它其实就是在逐一回答这四个问题:先是不管不顾地让程序直接跑,然后发现要分区,分区之后要处理地址转换,处理完还要面对碎片。整个演进过程,就是不断拆东墙补西墙、再补东墙的过程。理解了这一条主线,后面所有细节都顺理成章。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 分区管理:把内存切成块,各家用各家
2.1 固定分区:像宿舍分配一样简单,但浪费惊人
最早的多道程序存储管理方案是固定分区。思路很朴素:开机的时候把内存划分成几个固定大小的区域,每一个分区里放一个程序。比如把64KB的内存分成4个16KB的分区,每个分区住一个作业,谁进来谁住,互不打扰。
这个方案的好处是简单,操作系统只需要维护一张分区表,记录每个分区的起始地址和大小,就能完成分配。但它的问题一眼就能看出来:分区大小是写死的,如果来了一个20KB的程序,16KB的分区装不下,即使其他分区都闲着,你也只能干瞪眼。反过来,如果来了一个5KB的程序,分配给它一个16KB的分区,剩下的11KB就白白浪费了。
这种“分配给你的空间你用不完,多出来的部分又没法给别人用”的现象,就是内部碎片。固定分区是内部碎片的重灾区,因为分区大小和作业大小几乎不可能恰好相等。我当年学这块的时候就觉得,这设计太浪费了,为什么不等程序来了再决定分多大的空间?这正是动态分区要解决的。
2.2 动态分区:按需分配,但管理就复杂了
动态分区和固定分区的区别只有一点:分区的边界不是预先划死的,而是在装载程序时,根据程序的实际大小动态划定。比如内存现在空闲着10
