PintOS中全局数组内存加载问题:为何仅触发2次页错误?
PintOS VM开发中的页错误异常问题
问题说明
在PintOS VM模块开发过程中,编写了一个包含10页(4096*10)大小全局数组buf的用户程序,目的是通过对每个页首字节写入值来验证页错误处理逻辑。预期会触发10次页错误,但实际仅触发2次,且所有写入的值最终都存储在同一个物理页中。从程序输出可以看到,数组每个页的虚拟地址都对应不同的虚拟页,但物理页仅分配了1个。
代码片段
#define SIZE 10 * 4096 volatile char buf[SIZE] = {0}; int main (int argc, char**argv){ int cnt = 0; for(int i = 0; i < SIZE; i += 4096) { printf("%p", &buf[i]); buf[i] = 'a' ; } for(int i = 0; i < SIZE; i += 4096) { printf("%d %c\n", cnt ++, buf[i]); } }
程序输出
hello from page fault. hello from page fault. 0x804bce0 0x804cce0 0x804dce0 0x804ece0 0x804fce0 0x8050ce0 0x8051ce0 0x8052ce0 0x8053ce0 0x8054ce0 1 a 2 a 3 a 4 a 5 a 6 a 7 a 8 a 9 a
问题原因分析
1. 虚拟页分配符合预期
从输出的虚拟地址来看,&buf[i]依次相差0x1000(4096字节),说明全局数组确实占用了10个连续的虚拟页,虚拟地址空间的分配没有问题,问题出在物理页的映射逻辑上。
2. 页错误处理逻辑存在bug
核心问题是你的VM实现中,页错误处理函数没有为每个触发错误的虚拟页分配独立的物理页,而是重复将多个虚拟页映射到了同一个物理页:
- 第一次写入
buf[0]时触发页错误,此时你正确分配了一个物理页,并将对应的虚拟页映射到该物理页。 - 第二次写入
buf[4096]时再次触发页错误,但此时你的处理逻辑没有分配新的物理页,而是错误地将当前虚拟页也映射到了之前分配的那个物理页;或者你的逻辑在处理前两次页错误后,直接将数组对应的所有虚拟页都批量映射到了同一个物理页,导致后续写入不再触发页错误。 - 由于所有虚拟页都指向同一个物理页,后续对任何虚拟页首字节的写入都会覆盖同一个物理地址,最终所有
buf[i]读取到的都是同一个值。
3. 全局数组初始化的次要影响
你定义的buf是显式初始化为0的全局数组,这类数组在ELF文件中会被放入.data段(而非未初始化变量的.bss段)。如果你的VM实现对.data段采用了错误的批量映射策略,将整个10页的.data区域直接映射到单个物理页,也会导致后续写入都指向同一个物理页。
修复建议
- 检查页错误处理函数:确保每次处理页错误时,只针对当前触发错误的虚拟页分配物理页,并更新对应的页表项,避免批量映射多个虚拟页到同一个物理页。
- 验证虚拟页索引计算逻辑:确认在处理页错误时,正确提取了当前触发错误的虚拟地址对应的页索引,避免索引计算错误导致重复映射。
- 检查
.data段加载逻辑:确保加载.data段时,按照页粒度分配物理页,每个虚拟页对应独立的物理页(或在需要时通过写时复制分配新页)。
内容的提问来源于stack exchange,提问作者JungHwanPark
相关产品推荐
相关产品推荐

