OS-05 存储器管理
OS-05 存储器管理
一、程序的装入与链接
一个程序从源代码到在内存中执行,需要经历编译、链接和装入三个阶段。其中链接和装入方式会直接影响程序的执行效率和内存占用。
静态链接:在程序运行之前,将所有需要的目标模块和库函数链接成一个完整的可执行文件。优点是运行时不需额外加载,缺点是浪费空间(多个程序可能包含相同的库函数副本)且更新困难。
装入时动态链接:在将程序装入内存时,边装入边链接。用到的模块才被装入内存,未用到的模块不装入。这种方式灵活性更高。
运行时动态链接:程序在运行过程中,需要某个模块时才进行链接和装入。这是最灵活的方式,常用于插件系统和共享库。Linux中的共享库(.so文件)就是采用这种方式的典型例子。
二、可变分区分配算法
在可变分区分配中,内存被动态地划分为大小不同的分区。当需要为一个进程分配内存时,系统从空闲分区列表中选择一个合适的区域。
首次适应算法(FF):从内存低地址开始,选择第一个大小足够容纳进程的空闲分区。这是最简单的算法,实现速度快,但容易在低地址区域产生大量碎片。
最优适应算法(OF):从所有空闲分区中选择大小最接近进程需求的分区。这样可以减少大分区被分割的可能性,但会产生很多难以利用的小碎片。
最差适应算法(WF):选择最大的空闲分区。这样分割后剩余的空间仍然较大,可以容纳其他进程,但大分区很快就被分割成中等大小的分区,导致大进程无法分配。
三、分页式存储管理
分页是解决内存碎片问题的有效方法。它将物理内存划分为大小相等的块,称为"页框"或"帧"(Frame),将进程的逻辑地址空间也划分为同样大小的块,称为"页"(Page)。进程的各个页可以分散存放在不连续的页框中,通过"页表"来记录页与页框的映射关系。
3.1 地址变换过程
逻辑地址由两部分组成:[页号 | 页内偏移地址]。地址变换的过程是:
第一步:从逻辑地址中提取页号和页内偏移。这通过简单的除法和取模运算完成。
第二步:以页号为索引在页表中查找对应的页框号。
第三步:计算物理地址 = 页框号 × 页面大小 + 页内偏移。
例题:假设页面大小为4KB,页表内容为 {0→2, 1→4, 2→6, 3→8},求逻辑地址4100对应的物理地址。
页面大小 = 4KB = 4096B
页号 = 4100 / 4096 = 1(商)
页内偏移 = 4100 mod 4096 = 4(余数)
页表中页号1对应的页框号为4
物理地址 = 4 × 4096 + 4 = 16384 + 4 = 16388
3.2 快表(TLB)
由于页表存放在内存中,每次地址变换需要额外的一次内存访问(查页表),导致实际访存时间翻倍。TLB(快表)就是用来解决这个问题的。
TLB是一个容量很小但速度极快的硬件缓存,它存储了最近使用的页表项。在进行地址变换时,CPU首先检查TLB:
- 如果TLB命中(所需页号在TLB中),则直接得到页框号,无需访问内存中的页表
- 如果TLB未命中,则需要访问内存中的页表,并将新的映射关系加入TLB
TLB访问时间计算:
假设TLB访问时间为1ns,内存访问时间为100ns,TLB命中率为98%。则平均页面访问时间为:
平均时间 = 0.98 × (1 + 100) + 0.02 × (1 + 100 + 100) = 98.98 + 4.02 = 103ns
对比不使用TLB的情况:100 + 100 = 200ns。TLB将平均访问时间减少了近一半。
四、分段式存储管理
分段(Segmentation)是另一种存储管理方式。它从用户编程的角度出发,将一个程序划分为多个逻辑段:代码段、数据段、堆栈段等。每个段有独立的逻辑地址空间,从0开始编址。
逻辑地址结构:[段号 | 段内偏移]
与分页的关键区别:
- 分页是系统视角(为了管理便利),分段是用户视角(为了编程便利)
- 页面大小固定,段大小可变
- 分页是"看不见"的(对程序员透明),分段是"看得见"的(程序员需要感知段的存在)
- 分段便于实现共享和保护(可以为每个段设置独立的访问权限)
五、段页式存储管理
段页式结合了分段和分页的优点:先将程序按逻辑分段,再将每个段分成固定大小的页。地址变换需要两次查表:先查段表得到页表起始地址,再查页表得到页框号。虽然速度慢了一些,但兼顾了分段的逻辑清晰性和分页的存储效率。