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;
}
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