69天探索操作系统-第19天:理解操作系统中分页机制

257 阅读4分钟

pro10.avif

1.介绍

分页是一种内存管理方案,它消除了对连续分配物理内存的需要。它能避免外部碎片,并提供了一种高效的内存保护机制。分页的基本思想是将物理内存划分为固定大小的块,称为帧,以及逻辑内存划分为相同大小的块,称为页。

2.分页的基础概念

在分页中,内存地址被分为两部分:

  • 页号(p):用于访问页表中的相应帧
  • 页偏移(d):与帧号结合使用,以定义物理内存地址

计算例子:

对于一个具有4KB页面大小的32位地址空间:

  • 页面大小 = 4KB = 4096字节 = 2^12字节
  • 偏移量大小 = 12
  • 页面号码大小 = 32 - 12 = 20
  • 总共可能的页面数 = 2^20
  • 共可能的偏移值 = 2^12

3.页表架构

页表结构

struct page_table_entry {
    unsigned int frame_number : 20;  // Physical frame number
    unsigned int present : 1;        // Present bit
    unsigned int write : 1;          // Write permission
    unsigned int user : 1;           // User-mode access
    unsigned int accessed : 1;       // Page accessed
    unsigned int dirty : 1;          // Page modified
    unsigned int reserved : 7;       // Reserved bits
};

页表结构详细信息

每个条目包含:

  • 帧号(Frame Number):指向实际的物理内存帧
  • 当前/缺省位(Present/Absent bit):指示页面是否在内存中
  • 保护位:读取、写入、执行权限
  • 修改位:指示页面是否已更改
  • 引用位:显示页面是否已被访问
  • 缓存禁用:控制缓存行为

4. C实现

以下是一个基本分页系统的完整实现:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define PAGE_SIZE 4096
#define PAGE_TABLE_SIZE 1024
#define PHYSICAL_MEMORY_SIZE (PAGE_SIZE * PAGE_TABLE_SIZE)

typedef struct {
    unsigned int frame_number : 20;
    unsigned int present : 1;
    unsigned int write : 1;
    unsigned int user : 1;
    unsigned int accessed : 1;
    unsigned int dirty : 1;
    unsigned int reserved : 7;
} PageTableEntry;

typedef struct {
    PageTableEntry entries[PAGE_TABLE_SIZE];
} PageTable;

typedef struct {
    char data[PHYSICAL_MEMORY_SIZE];
} PhysicalMemory;

// Initialize page table
void init_page_table(PageTable *pt) {
    for (int i = 0; i < PAGE_TABLE_SIZE; i++) {
        pt->entries[i].frame_number = 0;
        pt->entries[i].present = 0;
        pt->entries[i].write = 1;
        pt->entries[i].user = 1;
        pt->entries[i].accessed = 0;
        pt->entries[i].dirty = 0;
        pt->entries[i].reserved = 0;
    }
}

// Translate virtual address to physical address
unsigned int translate_address(PageTable *pt, unsigned int virtual_address) {
    unsigned int page_number = virtual_address / PAGE_SIZE;
    unsigned int offset = virtual_address % PAGE_SIZE;
    
    if (page_number >= PAGE_TABLE_SIZE) {
        printf("Invalid page number\n");
        return -1;
    }
    
    if (!pt->entries[page_number].present) {
        printf("Page fault occurred\n");
        return -1;
    }
    
    return (pt->entries[page_number].frame_number * PAGE_SIZE) + offset;
}

int main() {
    PageTable *page_table = (PageTable*)malloc(sizeof(PageTable));
    PhysicalMemory *physical_memory = (PhysicalMemory*)malloc(sizeof(PhysicalMemory));
    
    // Initialize page table
    init_page_table(page_table);
    
    // Map some virtual pages to physical frames
    page_table->entries[0].frame_number = 0;
    page_table->entries[0].present = 1;
    
    page_table->entries[1].frame_number = 1;
    page_table->entries[1].present = 1;
    
    // Test address translation
    unsigned int virtual_address = 5000;  // Should be in second page
    unsigned int physical_address = translate_address(page_table, virtual_address);
    
    if (physical_address != -1) {
        printf("Virtual address %u maps to physical address %u\n", 
               virtual_address, physical_address);
    }
    
    free(page_table);
    free(physical_memory);
    return 0;
}

image.png

5. 性能考虑

TLB命中率影响

  • 现代处理器通常能够达到98-99%的TLB命中率
  • 每个TLB未命中的代价是50-100个CPU周期
  • 示例:对于99%的命中率和2个周期命中,50个周期未命中:平均访问时间 = 0.9 * 2 + 0.01 * 50 = 2.48周期

页面大小效应

  • 较大页面减少了所需的TLB条目数量
  • 常见大小:4KB(标准),2MB(超大页面),1GB(巨型页面)
  • 在内存浪费和TLB覆盖之间的权衡

多级页面表

  • 减少稀疏地址空间下的内存开销
  • 典型的x86-64使用4级页面表
  • 每级增加一次内存访问用于转换

6.真实案例

Linux实现

struct page {
    unsigned long flags;
    atomic_t _refcount;
    struct address_space *mapping;
    pgoff_t index;
    struct list_head lru;
    void *virtual;
};

Windows实现

  • 使用多级页面表
  • 支持大型页面(2MB)
  • 实现页面着色以优化缓存

7.总结

分页是现代操作系统中一个基本机制,提供:

  • 内存保护
  • 虚拟内存支持
  • 高效的内存利用
  • 进程隔离

页大小、TLB大小和页表结构之间的权衡随着新硬件架构的演变而不断发展。

8.参考资料和进一步阅读

  • Silberschatz, A., Galvin, P. B., & Gagne, G. (2018). Operating System Concepts, 10th Edition
  • Love, R. (2010). Linux Kernel Development, 3rd Edition
  • Intel® 64 and IA-32 Architectures Software Developer's Manual
  • AMD64 Architecture Programmer's Manual
  • Modern Operating Systems by Andrew S. Tanenbaum