前言
大家好!今天给大家带来《计算机操作系统》第四章 —— 存储器管理的全面解析。存储器管理是操作系统核心模块之一,直接决定了程序能否高效、安全地运行。本文会从基础概念到代码实现,用通俗易懂的语言拆解每个知识点,还会附上可直接运行的 C++代码,帮助大家彻底吃透这一章!
4.1 存储器的层次结构
核心概念
存储器的层次结构是为了解决「速度快的存储器贵、容量小,容量大的存储器速度慢、便宜」的矛盾,操作系统通过分层管理让 CPU 既能快速访问数据,又能使用大容量存储。
层次架构
核心要点
- 越靠近 CPU 的存储层级,速度越快、容量越小、单位成本越高;
- 操作系统的存储器管理主要针对主存储器(内存),虚拟存储器是内存 + 外存的抽象层;
- 层次结构的核心目标:用低成本实现接近高速缓存的访问速度、接近硬盘的存储容量。
4.2 程序的装入和链接
核心概念
程序从「磁盘上的可执行文件」到「内存中运行的进程」,需要经过装入(Load)和链接(Link)两个核心步骤:
- 链接:将多个目标文件、库文件合并为一个可执行文件(静态链接 / 动态链接);
- 装入:将可执行文件加载到内存,分配地址空间(绝对装入 / 可重定位装入 / 动态运行时装入)。
装入流程
代码实现(C++98 模拟程序装入过程)
#include <iostream> #include <vector> #include <string> // 严格兼容C++98标准,无任何C++11特性 using namespace std; // 定义目标文件结构体 struct ObjectFile { string name; // 目标文件名 vector<int> code; // 机器码(简化为整数数组) vector<int> data; // 数据段(简化为整数数组) }; // 定义可执行文件结构体 struct ExecFile { string name; // 可执行文件名 vector<int> text_segment; // 代码段 vector<int> data_segment; // 数据段 int base_addr; // 装入的基地址 }; // 静态链接:合并多个目标文件为可执行文件(C++98兼容版) ExecFile static_link(const vector<ObjectFile>& objs, const string& exec_name) { ExecFile exec; exec.name = exec_name; exec.base_addr = 0; // 初始基地址为0 // C++98不支持范围for循环,改用下标循环 for (size_t i = 0; i < objs.size(); ++i) { const ObjectFile& obj = objs[i]; // 合并代码段 exec.text_segment.insert(exec.text_segment.end(), obj.code.begin(), obj.code.end()); // 合并数据段 exec.data_segment.insert(exec.data_segment.end(), obj.data.begin(), obj.data.end()); } return exec; } // 可重定位装入:将可执行文件加载到指定内存地址 void relocate_load(ExecFile& exec, int new_base_addr) { exec.base_addr = new_base_addr; cout << "可执行文件[" << exec.name << "]装入内存,基地址:" << new_base_addr << endl; // 模拟地址重定位:代码段地址 = 基地址 + 偏移量 cout << "代码段地址重定位结果:" << endl; for (size_t i = 0; i < exec.text_segment.size(); ++i) { int old_addr = exec.text_segment[i]; exec.text_segment[i] = new_base_addr + static_cast<int>(i); // 偏移量为索引 cout << " 原地址:" << old_addr << " → 新地址:" << exec.text_segment[i] << endl; } } int main() { // 1. 模拟编译生成目标文件(C++98兼容:用push_back初始化vector) ObjectFile obj1, obj2; obj1.name = "main.o"; // 替代列表初始化:逐个添加元素 obj1.code.push_back(1001); obj1.code.push_back(1002); obj1.code.push_back(1003); obj1.data.push_back(10); obj1.data.push_back(20); obj1.data.push_back(30); obj2.name = "utils.o"; obj2.code.push_back(2001); obj2.code.push_back(2002); obj2.data.push_back(40); obj2.data.push_back(50); // 2. 静态链接 vector<ObjectFile> objs; objs.push_back(obj1); objs.push_back(obj2); ExecFile exec = static_link(objs, "app.exe"); cout << "静态链接完成,生成可执行文件:" << exec.name << endl; // 3. 可重定位装入(基地址设为0x1000) relocate_load(exec, 0x1000); return 0; }代码说明
- 用
ObjectFile模拟编译后的目标文件,包含代码段和数据段; static_link函数模拟静态链接,合并多个目标文件的段;relocate_load函数模拟可重定位装入,将可执行文件加载到指定基地址并完成地址重定位;- 代码完全兼容 C++98 标准,可直接编译运行(g++ 编译命令:
g++ -std=c++98 load_link.cpp -o load_link)。
运行结果
4.3 连续分配存储管理方式
核心概念
连续分配是最基础的内存管理方式,要求给进程分配连续的内存空间,主要分为:
- 单一连续分配:内存分为系统区和用户区,仅支持单进程(早期 OS);
- 固定分区分配:将内存划分为多个固定大小的分区,每个分区装一个进程;
- 动态分区分配:根据进程大小动态划分连续内存(首次适应 / 最佳适应 / 最坏适应算法)。
动态分区分配流程
代码实现(C++98 模拟动态分区分配)
#include <iostream> #include <vector> #include <string> #include <climits> #include <algorithm> // sort函数必需的头文件 // 严格遵循C++98标准 using namespace std; // 定义空闲分区结构体 struct FreeBlock { int start_addr; // 分区起始地址 int size; // 分区大小 FreeBlock(int s, int sz) : start_addr(s), size(sz) {} }; // ========== 关键修改:将比较结构体移到全局作用域 ========== // C++98不允许函数内局部类型作为sort的模板参数 struct CompareBlock { bool operator()(const FreeBlock& a, const FreeBlock& b) { return a.start_addr < b.start_addr; } }; // 定义进程结构体 struct Process { string pid; // 进程ID int size; // 所需内存大小 int alloc_addr; // 分配的起始地址 Process(string p, int sz) : pid(p), size(sz), alloc_addr(-1) {} }; // 空闲分区表(全局) vector<FreeBlock> free_blocks; // 首次适应算法:找第一个足够大的空闲分区 int first_fit(int req_size) { // 显式转换size_t为int,避免无符号/有符号比较警告 for (int i = 0; i < static_cast<int>(free_blocks.size()); ++i) { if (free_blocks[i].size >= req_size) { return i; // 返回分区索引 } } return -1; // 无可用分区 } // 最佳适应算法:找最小的足够大的空闲分区 int best_fit(int req_size) { int best_idx = -1; int min_size = INT_MAX; // 显式转换size_t为int,避免警告 for (int i = 0; i < static_cast<int>(free_blocks.size()); ++i) { if (free_blocks[i].size >= req_size && free_blocks[i].size < min_size) { min_size = free_blocks[i].size; best_idx = i; } } return best_idx; } // 分配内存 bool allocate_memory(Process& p, int algo_type) { int idx = -1; // 0=首次适应,1=最佳适应 if (algo_type == 0) { idx = first_fit(p.size); } else if (algo_type == 1) { idx = best_fit(p.size); } if (idx == -1) { cout << "进程[" << p.pid << "]分配内存失败:无足够空闲分区" << endl; return false; } // 分配内存 FreeBlock& block = free_blocks[idx]; p.alloc_addr = block.start_addr; cout << "进程[" << p.pid << "]分配成功:起始地址=" << block.start_addr << ",大小=" << p.size << endl; // 更新空闲分区表(分割分区) if (block.size > p.size) { // 剩余空间形成新的空闲分区 free_blocks.push_back(FreeBlock(block.start_addr + p.size, block.size - p.size)); } // 删除已分配的分区 free_blocks.erase(free_blocks.begin() + idx); return true; } // 释放内存并合并相邻空闲分区 void free_memory(Process& p) { if (p.alloc_addr == -1) { cout << "进程[" << p.pid << "]未分配内存,无需释放" << endl; return; } // 添加释放的分区到空闲表 free_blocks.push_back(FreeBlock(p.alloc_addr, p.size)); cout << "进程[" << p.pid << "]释放内存:起始地址=" << p.alloc_addr << ",大小=" << p.size << endl; p.alloc_addr = -1; // 合并相邻空闲分区(简化版:按起始地址排序后合并) // ========== 关键修改:使用全局的CompareBlock ========== sort(free_blocks.begin(), free_blocks.end(), CompareBlock()); // 合并逻辑 // 显式转换size_t为int,避免循环条件警告 for (int i = 0; i < static_cast<int>(free_blocks.size()) - 1; ) { FreeBlock& curr = free_blocks[i]; FreeBlock& next = free_blocks[i+1]; if (curr.start_addr + curr.size == next.start_addr) { // 合并两个分区 curr.size += next.size; free_blocks.erase(free_blocks.begin() + i + 1); } else { i++; } } // 打印合并后的空闲分区表 cout << "合并后空闲分区表:" << endl; for (size_t i = 0; i < free_blocks.size(); ++i) { cout << " 分区" << i+1 << ":起始地址=" << free_blocks[i].start_addr << ",大小=" << free_blocks[i].size << endl; } } // 打印空闲分区表 void print_free_blocks() { cout << "当前空闲分区表:" << endl; for (size_t i = 0; i < free_blocks.size(); ++i) { cout << " 分区" << i+1 << ":起始地址=" << free_blocks[i].start_addr << ",大小=" << free_blocks[i].size << endl; } } int main() { // 初始化空闲分区:总内存0~1000,初始为一个大分区 free_blocks.push_back(FreeBlock(0, 1000)); cout << "初始空闲分区表:" << endl; print_free_blocks(); // 创建进程 Process p1("P1", 200); Process p2("P2", 300); Process p3("P3", 400); // 首次适应算法分配P1 cout << "\n--- 首次适应分配P1(200)---" << endl; allocate_memory(p1, 0); print_free_blocks(); // 最佳适应算法分配P2 cout << "\n--- 最佳适应分配P2(300)---" << endl; allocate_memory(p2, 1); print_free_blocks(); // 释放P1 cout << "\n--- 释放P1 ---" << endl; free_memory(p1); // 最佳适应算法分配P3 cout << "\n--- 最佳适应分配P3(400)---" << endl; allocate_memory(p3, 1); print_free_blocks(); return 0; }代码说明
FreeBlock模拟空闲分区,Process模拟进程;- 实现了首次适应和最佳适应两种核心分配算法;
- 释放内存时自动合并相邻空闲分区(解决内存碎片问题);
- 代码兼容 C++98:使用
vector、自定义排序比较函数(C++98 不支持 lambda)、INT_MAX等; - 编译命令:
g++ -std=c++98 continuous_alloc.cpp -o continuous_alloc。
运行结果
4.4 对换 (Swapping)
核心概念
对换(也叫交换)是解决内存不足的核心技术:
- 内存紧张时,将暂时不运行的进程完整换出到外存(交换区),释放内存;
- 需要运行时,再将进程换入到内存;
- 核心目标:提高内存利用率,支持更多进程并发。
对换流程
代码实现(C++98 模拟对换机制)
#include <iostream> #include <vector> #include <string> #include <queue> // 严格C++98标准 using namespace std; // 进程状态枚举(C++98不支持enum class,用普通枚举) enum ProcessState { RUNNING, // 内存中运行 WAITING, // 等待换入 SWAPPED // 换出到外存 }; // 进程结构体 struct Process { string pid; // 进程ID int size; // 内存大小 int priority; // 优先级(数值越小优先级越高) int run_time; // 已运行时间 ProcessState state; // 状态 Process(string p, int sz, int pri) : pid(p), size(sz), priority(pri), run_time(0), state(WAITING) {} }; // 内存结构体 struct Memory { int total_size; // 总大小 int used_size; // 已用大小 vector<Process*> running_procs; // 内存中的进程 Memory(int sz) : total_size(sz), used_size(0) {} }; // 外存交换区 struct SwapArea { int total_size; // 总大小 int used_size; // 已用大小 vector<Process*> swapped_procs; // 换出的进程 SwapArea(int sz) : total_size(sz), used_size(0) {} }; // 选择换出进程(按优先级:优先级低的先换出;优先级相同则运行时间长的先换出) Process* select_swap_out_proc(Memory& mem) { Process* swap_proc = NULL; int max_priority = -1; int max_run_time = -1; // ========== 修复:替换C++11范围for为C++98迭代器循环 ========== for (vector<Process*>::iterator it = mem.running_procs.begin(); it != mem.running_procs.end(); ++it) { Process* p = *it; if (p->priority > max_priority || (p->priority == max_priority && p->run_time > max_run_time)) { max_priority = p->priority; max_run_time = p->run_time; swap_proc = p; } } return swap_proc; } // 换出进程到外存 bool swap_out(Memory& mem, SwapArea& swap, Process* p) { if (swap.used_size + p->size > swap.total_size) { cout << "交换区空间不足,换出进程[" << p->pid << "]失败" << endl; return false; } // 从内存移除 for (vector<Process*>::iterator it = mem.running_procs.begin(); it != mem.running_procs.end(); ++it) { if ((*it)->pid == p->pid) { mem.running_procs.erase(it); break; } } mem.used_size -= p->size; p->state = SWAPPED; // 加入交换区 swap.swapped_procs.push_back(p); swap.used_size += p->size; cout << "进程[" << p->pid << "]换出成功!内存释放:" << p->size << ",交换区占用:" << swap.used_size << endl; return true; } // 换入进程到内存 bool swap_in(Memory& mem, SwapArea& swap, Process* p) { if (mem.used_size + p->size > mem.total_size) { // 内存不足,先换出一个进程 Process* swap_out_proc = select_swap_out_proc(mem); if (swap_out_proc == NULL) { cout << "无可用进程换出,换入进程[" << p->pid << "]失败" << endl; return false; } if (!swap_out(mem, swap, swap_out_proc)) { return false; } } // 从交换区移除 for (vector<Process*>::iterator it = swap.swapped_procs.begin(); it != swap.swapped_procs.end(); ++it) { if ((*it)->pid == p->pid) { swap.swapped_procs.erase(it); break; } } swap.used_size -= p->size; p->state = RUNNING; // 加入内存 mem.running_procs.push_back(p); mem.used_size += p->size; cout << "进程[" << p->pid << "]换入成功!内存占用:" << mem.used_size << endl; return true; } // 打印系统状态 void print_system_state(Memory& mem, SwapArea& swap) { cout << "\n=== 系统状态 ===" << endl; cout << "内存:总大小=" << mem.total_size << ",已用=" << mem.used_size << endl; cout << " 运行中进程:"; // ========== 修复:替换C++11范围for为C++98迭代器循环 ========== for (vector<Process*>::iterator it = mem.running_procs.begin(); it != mem.running_procs.end(); ++it) { Process* p = *it; cout << p->pid << "(" << p->size << ") "; } cout << endl; cout << "交换区:总大小=" << swap.total_size << ",已用=" << swap.used_size << endl; cout << " 换出进程:"; // ========== 修复:替换C++11范围for为C++98迭代器循环 ========== for (vector<Process*>::iterator it = swap.swapped_procs.begin(); it != swap.swapped_procs.end(); ++it) { Process* p = *it; cout << p->pid << "(" << p->size << ") "; } cout << "\n=================\n" << endl; } int main() { // 初始化内存(总大小1000)和交换区(总大小2000) Memory mem(1000); SwapArea swap(2000); // 创建进程 Process p1("P1", 300, 1); // 高优先级 Process p2("P2", 400, 2); // 中优先级 Process p3("P3", 500, 3); // 低优先级 // 换入P1 cout << "--- 换入P1(300)---" << endl; swap_in(mem, swap, &p1); print_system_state(mem, swap); // 换入P2 cout << "--- 换入P2(400)---" << endl; swap_in(mem, swap, &p2); print_system_state(mem, swap); // 换入P3(内存不足,触发换出) cout << "--- 换入P3(500)---" << endl; p2.run_time = 10; // P2运行时间长,优先换出 swap_in(mem, swap, &p3); print_system_state(mem, swap); // 换入P2 cout << "--- 换入P2(400)---" << endl; swap_in(mem, swap, &p2); print_system_state(mem, swap); return 0; }代码说明
- 定义
Process结构体,包含优先级、运行时间、状态等核心属性; Memory模拟内存,SwapArea模拟外存交换区;swap_out实现进程换出,swap_in实现进程换入(内存不足时自动触发换出);- 换出策略:优先换出优先级低、运行时间长的进程;
- 编译命令:
g++ -std=c++98 swapping.cpp -o swapping。
运行结果
4.5 分页存储管理方式
核心概念
分页是解决连续分配「内存碎片」问题的核心方案:
- 内存划分为固定大小的页框(Page Frame)(如 4KB);
- 进程地址空间划分为大小相同的页(Page);
- 进程的页可以离散分配到内存的页框中,通过页表映射页和页框的对应关系;
- 核心优势:无外部碎片,仅少量内部碎片(最后一页未占满的部分)。
分页架构
代码实现(C++98 模拟分页存储管理)
#include <iostream> #include <vector> #include <string> #include <map> // C++98标准 using namespace std; // 页大小(固定为4KB) const int PAGE_SIZE = 4096; // 页表项结构体 struct PageTableEntry { int frame_num; // 页框号 bool valid; // 有效位(是否在内存中) PageTableEntry() : frame_num(-1), valid(false) {} }; // 进程结构体 struct Process { string pid; // 进程ID int size; // 进程大小 vector<PageTableEntry> pt; // 页表 Process(string p, int sz) : pid(p), size(sz) { // 计算页数:向上取整 int page_count = (sz + PAGE_SIZE - 1) / PAGE_SIZE; pt.resize(page_count); // 初始化页表 } }; // 内存页框管理器 struct FrameManager { int total_frames; // 总页框数 vector<bool> frame_used; // 页框使用状态(true=已用) FrameManager(int total_size) : total_frames(total_size / PAGE_SIZE) { frame_used.resize(total_frames, false); // 初始全空闲 } // 分配空闲页框 int allocate_frame() { for (int i = 0; i < total_frames; ++i) { if (!frame_used[i]) { frame_used[i] = true; return i; } } return -1; // 无空闲页框 } // 释放页框 void free_frame(int frame_num) { if (frame_num >= 0 && frame_num < total_frames) { frame_used[frame_num] = false; cout << "页框" << frame_num << "释放成功" << endl; } } // 打印页框使用状态 void print_frames() { cout << "页框使用状态(总" << total_frames << "个):"; for (int i = 0; i < total_frames; ++i) { cout << (frame_used[i] ? "1" : "0"); } cout << endl; } }; // 虚拟地址转物理地址 int virtual_to_physical(Process& p, int virtual_addr) { // 拆分虚拟地址:页号 = 虚拟地址 / 页大小,页内偏移 = 虚拟地址 % 页大小 int page_num = virtual_addr / PAGE_SIZE; int offset = virtual_addr % PAGE_SIZE; // 检查页号是否合法 if (page_num >= (int)p.pt.size()) { cout << "进程[" << p.pid << "]虚拟地址" << virtual_addr << ":页号" << page_num << "越界" << endl; return -1; } // 检查有效位 PageTableEntry& pte = p.pt[page_num]; if (!pte.valid) { cout << "进程[" << p.pid << "]虚拟地址" << virtual_addr << ":页" << page_num << "不在内存中(缺页)" << endl; return -1; } // 计算物理地址 = 页框号 * 页大小 + 页内偏移 int physical_addr = pte.frame_num * PAGE_SIZE + offset; cout << "进程[" << p.pid << "]虚拟地址" << virtual_addr << " → 页号" << page_num << ",偏移" << offset << " → 物理地址" << physical_addr << endl; return physical_addr; } // 分配进程的页到内存页框 bool allocate_pages(Process& p, FrameManager& fm) { cout << "为进程[" << p.pid << "]分配页(总页数:" << p.pt.size() << ")" << endl; for (int i = 0; i < (int)p.pt.size(); ++i) { int frame = fm.allocate_frame(); if (frame == -1) { cout << "页框不足,进程[" << p.pid << "]分配失败" << endl; // 回滚已分配的页框 for (int j = 0; j < i; ++j) { fm.free_frame(p.pt[j].frame_num); p.pt[j].valid = false; } return false; } p.pt[i].frame_num = frame; p.pt[i].valid = true; cout << " 页" << i << " → 页框" << frame << endl; } return true; } int main() { // 初始化内存:总大小32KB → 8个页框(4KB/页) FrameManager fm(32 * 1024); cout << "初始页框状态:" << endl; fm.print_frames(); // 创建进程:大小10KB → 需要3页(4+4+2) Process p1("P1", 10 * 1024); // 分配页 cout << "\n--- 分配P1的页 ---" << endl; allocate_pages(p1, fm); fm.print_frames(); // 虚拟地址转物理地址 cout << "\n--- 虚拟地址转换 ---" << endl; virtual_to_physical(p1, 1000); // 页0,偏移1000 virtual_to_physical(p1, 5000); // 页1,偏移904 virtual_to_physical(p1, 9000); // 页2,偏移808 virtual_to_physical(p1, 13000); // 页3,越界 // 释放进程的页框 cout << "\n--- 释放P1的页框 ---" << endl; for (int i = 0; i < (int)p1.pt.size(); ++i) { fm.free_frame(p1.pt[i].frame_num); p1.pt[i].valid = false; } fm.print_frames(); return 0; }代码说明
- 固定页大小为 4KB,
FrameManager管理内存页框的分配 / 释放; Process包含页表,页表项记录页框号和有效位;virtual_to_physical实现虚拟地址到物理地址的转换(核心逻辑);- 模拟「缺页」「地址越界」等异常场景;
- 编译命令:
g++ -std=c++98 paging.cpp -o paging。
运行结果
4.6 分段存储管理方式
核心概念
分段是按程序的逻辑结构划分内存(如代码段、数据段、栈段),每个段是连续的内存块,不同段可离散分配:
- 段:有意义的逻辑单位,大小不固定(如代码段 10KB、栈段 5KB);
- 段表:记录每个段的基地址和段长,实现虚拟地址到物理地址的映射;
- 核心优势:便于程序的共享和保护(如代码段只读、数据段可读写)。
分段架构
代码实现(C++98 模拟分段存储管理)
#include <iostream> #include <vector> #include <string> #include <map> // C++98标准 using namespace std; // 段表项结构体 struct SegmentTableEntry { string seg_name; // 段名(代码段/数据段/栈段) int base_addr; // 段基地址 int seg_size; // 段长度 string protect; // 保护属性(R-只读,RW-读写,X-执行) bool valid; // 有效位 SegmentTableEntry(string name, int size, string prot) : seg_name(name), seg_size(size), protect(prot), base_addr(-1), valid(false) {} }; // 进程结构体 struct Process { string pid; // 进程ID vector<SegmentTableEntry> seg_table; // 段表 Process(string p) : pid(p) {} // 添加段 void add_segment(string name, int size, string protect) { seg_table.push_back(SegmentTableEntry(name, size, protect)); } }; // 内存管理器 struct MemoryManager { int total_size; // 总内存大小 int used_size; // 已用大小 MemoryManager(int sz) : total_size(sz), used_size(0) {} // 分配连续内存段 int allocate_segment(int size) { if (used_size + size > total_size) { return -1; // 内存不足 } int base = used_size; used_size += size; return base; } // 释放段(简化版:不合并,仅标记内存可用) void free_segment(int base, int size) { cout << "释放段:基地址=" << base << ",大小=" << size << endl; // 实际OS中需要管理空闲分区,此处简化为打印 used_size -= size; } }; // 虚拟地址转物理地址 int virtual_to_physical(Process& p, MemoryManager& mm, int seg_num, int offset) { // 检查段号是否合法 if (seg_num >= (int)p.seg_table.size()) { cout << "进程[" << p.pid << "]段号" << seg_num << "越界" << endl; return -1; } SegmentTableEntry& ste = p.seg_table[seg_num]; // 检查有效位 if (!ste.valid) { cout << "进程[" << p.pid << "]段" << ste.seg_name << "不在内存中" << endl; return -1; } // 检查段内偏移是否越界 if (offset >= ste.seg_size) { cout << "进程[" << p.pid << "]段" << ste.seg_name << "内偏移" << offset << "越界(段长:" << ste.seg_size << ")" << endl; return -1; } // 计算物理地址 int physical_addr = ste.base_addr + offset; cout << "进程[" << p.pid << "]段" << ste.seg_name << "(段号" << seg_num << ")偏移" << offset << " → 物理地址" << physical_addr << "(保护属性:" << ste.protect << ")" << endl; return physical_addr; } // 分配进程的段到内存 bool allocate_segments(Process& p, MemoryManager& mm) { cout << "为进程[" << p.pid << "]分配段:" << endl; for (int i = 0; i < (int)p.seg_table.size(); ++i) { SegmentTableEntry& ste = p.seg_table[i]; int base = mm.allocate_segment(ste.seg_size); if (base == -1) { cout << "内存不足,进程[" << p.pid << "]段" << ste.seg_name << "分配失败" << endl; // 回滚已分配的段 for (int j = 0; j < i; ++j) { mm.free_segment(p.seg_table[j].base_addr, p.seg_table[j].seg_size); p.seg_table[j].valid = false; } return false; } ste.base_addr = base; ste.valid = true; cout << " 段" << i << "(" << ste.seg_name << "):基地址=" << base << ",大小=" << ste.seg_size << endl; } return true; } int main() { // 初始化内存(总大小20KB) MemoryManager mm(20 * 1024); cout << "初始内存:总大小=" << mm.total_size << ",已用=" << mm.used_size << endl; // 创建进程并添加段 Process p1("P1"); p1.add_segment("代码段", 5 * 1024, "X"); // 执行权限 p1.add_segment("数据段", 8 * 1024, "RW"); // 读写权限 p1.add_segment("栈段", 3 * 1024, "RW"); // 读写权限 // 分配段 cout << "\n--- 分配P1的段 ---" << endl; allocate_segments(p1, mm); cout << "分配后内存:已用=" << mm.used_size << endl; // 虚拟地址转物理地址 cout << "\n--- 虚拟地址转换 ---" << endl; virtual_to_physical(p1, mm, 0, 1000); // 代码段,偏移1000 virtual_to_physical(p1, mm, 1, 2000); // 数据段,偏移2000 virtual_to_physical(p1, mm, 2, 4000); // 栈段,偏移4000(越界) virtual_to_physical(p1, mm, 3, 1000); // 段号3(越界) return 0; }代码说明
SegmentTableEntry包含段名、基地址、段长、保护属性等核心字段;MemoryManager管理连续内存段的分配 / 释放;virtual_to_physical实现分段地址转换,包含越界检查和保护属性校验;- 模拟代码段(执行权限)、数据段 / 栈段(读写权限)的不同保护策略;
- 编译命令:
g++ -std=c++98 segmentation.cpp -o segmentation。
运行结果
习题
基础题
- 简述存储器层次结构的设计思想,为什么需要分层管理?
- 对比连续分配、分页、分段三种存储管理方式的优缺点。
- 分页和分段的核心区别是什么?(提示:页是物理划分,段是逻辑划分)
- 对换技术的核心作用是什么?换出进程的选择策略有哪些?
编程题
- 基于本文的分页代码,实现「最坏适应算法」的页框分配策略。
- 基于本文的分段代码,添加「段共享」功能(多个进程共享同一个代码段)。
- 扩展对换代码,实现「按进程剩余运行时间」选择换出进程的策略。
总结
核心知识点回顾
- 存储器层次结构:核心是「速度 - 容量 - 成本」的平衡,OS 主要管理主存,虚拟内存是内存 + 外存的抽象;
- 存储管理方式:
- 连续分配:简单但有外部碎片,适合早期单进程 OS;
- 分页:离散分配,无外部碎片,按物理大小划分;
- 分段:离散分配,按逻辑结构划分,便于共享和保护;
- 核心技术:程序装入链接是进程运行的前提,对换技术解决内存不足问题,地址转换(分页 / 分段)是虚拟地址到物理地址的核心。