内存管理

内存管理是操作系统的核心功能之一,负责高效、安全地为进程分配和回收内存资源。

内存层次

┌────────────────────────────────────┐
│           CPU寄存器                 │ ◄─ 访问速度最快,容量最小
├────────────────────────────────────┤
│          L1/L2/L3 缓存             │ ◄─ 几十KB~几MB
├────────────────────────────────────�
│            主存 (RAM)              │ ◄─ 几GB~几百GB
├────────────────────────────────────┤
│        磁盘存储 (SSD/HDD)          │ ◄─ TB级别,访问最慢
└────────────────────────────────────┘

连续内存分配

固定分区分配

将内存划分为固定大小的分区:

// 固定分区示例
typedef struct {
    int id;           // 分区ID
    int size;         // 分区大小
    int start_addr;   // 起始地址
    int process_id;   // 占用进程,-1表示空闲
} Partition;
 
Partition partitions[] = {
    {0, 64, 0, -1},      // 64KB分区
    {1, 64, 64, 3},      // 已被进程3占用
    {2, 128, 192, -1},    // 128KB分区
    // ...
};

问题:内部碎片(分配 > 需要)、分区数量固定

动态分区分配

根据进程实际需求分配连续空间。

首次适配(First Fit)

#include <stdbool.h>
 
typedef struct hole {
    int start;
    int size;
    struct hole *next;
} Hole;
 
Hole *memory = NULL;  // 内存孔链表
 
void *first_fit(int process_size) {
    Hole *prev = NULL;
    Hole *curr = memory;
    
    while (curr != NULL) {
        if (curr->size >= process_size) {
            // 找到足够大的孔
            if (curr->size == process_size) {
                // 完全匹配,直接移除孔
                if (prev)
                    prev->next = curr->next;
                else
                    memory = curr->next;
            } else {
                // 分割孔
                curr->start += process_size;
                curr->size -= process_size;
            }
            return (void *)curr->start;
        }
        prev = curr;
        curr = curr->next;
    }
    return NULL;  // 没有足够大的孔
}

最佳适配(Best Fit)

void *best_fit(int process_size) {
    Hole *best = NULL;
    Hole *prev = NULL;
    Hole *curr = memory;
    int min_remaining = __INT_MAX__;
    
    while (curr != NULL) {
        if (curr->size >= process_size) {
            int remaining = curr->size - process_size;
            if (remaining < min_remaining) {
                min_remaining = remaining;
                best = curr;
            }
        }
        prev = curr;
        curr = curr->next;
    }
    
    if (best) {
        void *addr = (void *)best->start;
        best->start += process_size;
        best->size -= process_size;
        return addr;
    }
    return NULL;
}

虚拟内存

分页(Paging)

将进程的虚拟地址空间划分为固定大小的页,物理内存划分为同样大小的帧:

虚拟地址: [页号 | 页内偏移]
          ↓
        分页机制
          ↓
物理地址: [帧号 | 页内偏移]
// 虚拟地址到物理地址转换
typedef struct {
    unsigned int page_number;
    unsigned int offset;
} virtual_addr;
 
typedef struct {
    unsigned int frame_number;
    unsigned int valid;     // 该页是否在内存中
    unsigned int dirty;     // 是否被修改过
    unsigned int reference;  // 访问位
} page_table_entry;
 
page_table_entry page_table[MAX_PAGES];
 
// 地址转换
unsigned long translate(unsigned long virtual_addr) {
    virtual_addr va;
    va.value = virtual_addr;
    
    page_table_entry *entry = &page_table[va.page_number];
    
    if (!entry->valid) {
        // 触发缺页中断
        handle_page_fault(va.page_number);
        entry = &page_table[va.page_number];
    }
    
    entry->reference = 1;  // 设置引用位
    
    return (entry->frame_number << PAGE_SHIFT) | va.offset;
}

多级页表

// 两级页表结构
typedef struct {
    unsigned int p1_index;  // 一级页表索引
    unsigned int p2_index;  // 二级页表索引
    unsigned int offset;     // 页内偏移
} two_level_addr;
 
// 地址转换
unsigned long two_level_translate(two_level_addr va, unsigned long pgd_base) {
    // 获取一级页表项
    unsigned long *pgd = (unsigned long *)pgd_base;
    unsigned long pde = pgd[va.p1_index];
    
    if (!(pde & 1)) {
        // 一级页表项无效,需要分配二级页表
        allocate_page_table(va.p1_index);
        pde = pgd[va.p1_index];
    }
    
    // 获取二级页表项
    unsigned long *pte = (unsigned long *)(pde & ~0xFFF);
    unsigned long pte_entry = pte[va.p2_index];
    
    if (!(pte_entry & 1)) {
        // 二级页表项无效,缺页
        handle_page_fault(va.p1_index, va.p2_index);
        pte_entry = pte[va.p2_index];
    }
    
    return ((pte_entry & ~0xFFF) << PAGE_SHIFT) | va.offset;
}

页面置换算法

LRU(最近最少使用)

#include <stdlib.h>
#include <stdbool.h>
 
typedef struct lru_node {
    int page_number;
    struct lru_node *prev;
    struct lru_node *next;
} lru_node;
 
lru_node *lru_list_head = NULL;  // 链表头(最近使用)
lru_node *lru_list_tail = NULL;  // 链表尾(最久未使用)
int lru_frames[NUM_FRAMES];
int frame_count = 0;
 
// 访问页面
void access_page(int page_num) {
    lru_node *node = find_in_list(page_num);
    
    if (node) {
        // 已在内存中,移动到链表头
        move_to_head(node);
    } else {
        // 缺页
        if (frame_count >= NUM_FRAMES) {
            // 需要置换
            int victim = lru_list_tail->page_number;
            remove_tail();
            evict_page(victim);
        }
        add_to_head(create_node(page_num));
        load_page(page_num);
    }
}

时钟算法(Clock Algorithm)

// 时钟页面置换算法
typedef struct {
    int page_number;
    unsigned int use_bit;    // 使用位(访问位)
    unsigned int dirty_bit;  // 脏位
} frame_entry;
 
frame_entry frames[NUM_FRAMES];
int hand = 0;  // 指针位置
 
int clock_algorithm() {
    while (true) {
        frame_entry *frame = &frames[hand];
        
        if (frame->use_bit == 0) {
            // 选择该页置换
            int victim = hand;
            hand = (hand + 1) % NUM_FRAMES;
            return victim;
        }
        
        // 清除使用位,继续查找
        frame->use_bit = 0;
        hand = (hand + 1) % NUM_FRAMES;
    }
}

段页式存储

结合分段和分页的优点:

逻辑地址: [段号 | 段内偏移]
              ↓
段表查找 → 得到段基址和限长
              ↓
虚拟地址: [页号 | 页内偏移]
              ↓
页表查找 → 得到物理帧号
              ↓
物理地址: [物理帧号 | 页内偏移]

内存分配算法

伙伴系统(Buddy System)

typedef struct block {
    int order;              // 阶(2^order字节)
    int size;
    struct block *next;
} block;
 
block *free_lists[MAX_ORDER + 1];
 
block *buddy_alloc(int size) {
    int order = ceil_log2(size);
    
    for (int o = order; o <= MAX_ORDER; o++) {
        if (free_lists[o] != NULL) {
            block *block = free_lists[o];
            free_lists[o] = block->next;
            
            // 分割成更小的块
            while (o > order) {
                o--;
                block *buddy = block + (1 << o);
                buddy->order = o;
                buddy->size = 1 << o;
                buddy->next = free_lists[o];
                free_lists[o] = buddy;
            }
            
            block->order = order;
            return block;
        }
    }
    return NULL;  // 分配失败
}
 
void buddy_free(block *block) {
    int order = block->order;
    
    while (order < MAX_ORDER) {
        // 计算伙伴地址
        unsigned long addr = (unsigned long)block;
        unsigned long buddy_addr = addr ^ (1 << order);
        block *buddy = (block *)buddy_addr;
        
        // 检查伙伴是否空闲且大小相同
        if (is_free(buddy) && buddy->order == order) {
            remove_from_free_list(buddy);
            // 合并
            block = (addr < buddy_addr) ? block : buddy;
            order++;
        } else {
            break;
        }
    }
    
    // 加入空闲链表
    block->order = order;
    add_to_free_list(block);
}

参考