《计算机操作系统》第四章-存储器管理

前言

大家好!今天给大家带来《计算机操作系统》第四章 ------ 存储器管理的全面解析。存储器管理是操作系统核心模块之一,直接决定了程序能否高效、安全地运行。本文会从基础概念到代码实现,用通俗易懂的语言拆解每个知识点,还会附上可直接运行的 C++代码,帮助大家彻底吃透这一章!

4.1 存储器的层次结构

核心概念

存储器的层次结构是为了解决「速度快的存储器贵、容量小,容量大的存储器速度慢、便宜」的矛盾,操作系统通过分层管理让 CPU 既能快速访问数据,又能使用大容量存储。

层次架构

核心要点

  1. 越靠近 CPU 的存储层级,速度越快、容量越小、单位成本越高;
  2. 操作系统的存储器管理主要针对主存储器(内存) ,虚拟存储器是内存 + 外存的抽象层;
  3. 层次结构的核心目标:用低成本实现接近高速缓存的访问速度、接近硬盘的存储容量。

4.2 程序的装入和链接

核心概念

程序从「磁盘上的可执行文件」到「内存中运行的进程」,需要经过装入 (Load)和链接(Link)两个核心步骤:

  • 链接:将多个目标文件、库文件合并为一个可执行文件(静态链接 / 动态链接);
  • 装入:将可执行文件加载到内存,分配地址空间(绝对装入 / 可重定位装入 / 动态运行时装入)。
装入流程

代码实现(C++98 模拟程序装入过程)

cpp 复制代码
#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;
}

代码说明

  1. ObjectFile模拟编译后的目标文件,包含代码段和数据段;
  2. static_link函数模拟静态链接,合并多个目标文件的段;
  3. relocate_load函数模拟可重定位装入,将可执行文件加载到指定基地址并完成地址重定位;
  4. 代码完全兼容 C++98 标准,可直接编译运行(g++ 编译命令:g++ -std=c++98 load_link.cpp -o load_link)。

运行结果


4.3 连续分配存储管理方式

核心概念

连续分配是最基础的内存管理方式,要求给进程分配连续的内存空间,主要分为:

  • 单一连续分配:内存分为系统区和用户区,仅支持单进程(早期 OS);
  • 固定分区分配:将内存划分为多个固定大小的分区,每个分区装一个进程;
  • 动态分区分配:根据进程大小动态划分连续内存(首次适应 / 最佳适应 / 最坏适应算法)。
动态分区分配流程

代码实现(C++98 模拟动态分区分配)

cpp 复制代码
#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;
}

代码说明

  1. FreeBlock模拟空闲分区,Process模拟进程;
  2. 实现了首次适应最佳适应两种核心分配算法;
  3. 释放内存时自动合并相邻空闲分区(解决内存碎片问题);
  4. 代码兼容 C++98:使用vector、自定义排序比较函数(C++98 不支持 lambda)、INT_MAX等;
  5. 编译命令:g++ -std=c++98 continuous_alloc.cpp -o continuous_alloc

运行结果


4.4 对换 (Swapping)

核心概念

对换(也叫交换)是解决内存不足的核心技术:

  • 内存紧张时,将暂时不运行的进程完整换出到外存(交换区),释放内存;
  • 需要运行时,再将进程换入到内存;
  • 核心目标:提高内存利用率,支持更多进程并发。
对换流程

代码实现(C++98 模拟对换机制)

cpp 复制代码
#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;
}

代码说明

  1. 定义Process结构体,包含优先级、运行时间、状态等核心属性;
  2. Memory模拟内存,SwapArea模拟外存交换区;
  3. swap_out实现进程换出,swap_in实现进程换入(内存不足时自动触发换出);
  4. 换出策略:优先换出优先级低、运行时间长的进程;
  5. 编译命令: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;
}

代码说明

  1. 固定页大小为 4KB,FrameManager管理内存页框的分配 / 释放;
  2. Process包含页表,页表项记录页框号和有效位;
  3. virtual_to_physical实现虚拟地址到物理地址的转换(核心逻辑);
  4. 模拟「缺页」「地址越界」等异常场景;
  5. 编译命令: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;
}

代码说明

  1. SegmentTableEntry包含段名、基地址、段长、保护属性等核心字段;
  2. MemoryManager管理连续内存段的分配 / 释放;
  3. virtual_to_physical实现分段地址转换,包含越界检查保护属性校验
  4. 模拟代码段(执行权限)、数据段 / 栈段(读写权限)的不同保护策略;
  5. 编译命令:g++ -std=c++98 segmentation.cpp -o segmentation

运行结果


习题

基础题

  1. 简述存储器层次结构的设计思想,为什么需要分层管理?
  2. 对比连续分配、分页、分段三种存储管理方式的优缺点。
  3. 分页和分段的核心区别是什么?(提示:页是物理划分,段是逻辑划分)
  4. 对换技术的核心作用是什么?换出进程的选择策略有哪些?

编程题

  1. 基于本文的分页代码,实现「最坏适应算法」的页框分配策略。
  2. 基于本文的分段代码,添加「段共享」功能(多个进程共享同一个代码段)。
  3. 扩展对换代码,实现「按进程剩余运行时间」选择换出进程的策略。

总结

核心知识点回顾

  1. 存储器层次结构:核心是「速度 - 容量 - 成本」的平衡,OS 主要管理主存,虚拟内存是内存 + 外存的抽象;
  2. 存储管理方式
    • 连续分配:简单但有外部碎片,适合早期单进程 OS;
    • 分页:离散分配,无外部碎片,按物理大小划分;
    • 分段:离散分配,按逻辑结构划分,便于共享和保护;
  3. 核心技术:程序装入链接是进程运行的前提,对换技术解决内存不足问题,地址转换(分页 / 分段)是虚拟地址到物理地址的核心。
相关推荐
semantist@语校2 小时前
第六十篇|语言学校 Prompt 工程化实践:从字段解释到判断边界的结构设计(以日生日本语学园为例)
大数据·数据库·人工智能·百度·ai·prompt·知识图谱
AomanHao2 小时前
【ISP】图像质量评价指标-NIQE
人工智能·机器学习
tobias.b2 小时前
408真题解析-2010-11-数据结构-基础排序算法特征
数据结构·算法·排序算法·计算机考研·408真题解析
sali-tec2 小时前
C# 基于OpenCv的视觉工作流-章14-轮廓提取
人工智能·opencv·算法·计算机视觉
晨非辰2 小时前
Linux权限实战速成:用户切换/文件控制/安全配置15分钟掌握,解锁核心操作与权限模型内核逻辑
linux·运维·服务器·c++·人工智能·后端
草莓熊Lotso2 小时前
Linux 进程创建与终止全解析:fork 原理 + 退出机制实战
linux·运维·服务器·开发语言·汇编·c++·人工智能
Agentcometoo2 小时前
2026 AI 元年|智能体来了:Agent Native 正在取代 Copilot,定义下一代 AI 公司
人工智能
东华果汁哥2 小时前
【机器视觉 视频截帧算法】OpenCV 视频截帧算法教程
opencv·算法·音视频
weixin_669545203 小时前
持续2.7A峰值5A有刷直流马达正反转驱动芯片TC1305E
人工智能·嵌入式硬件·硬件工程·信息与通信