为何磁盘读取函数会引发页错误?FAT BPB读取故障排查
磁盘读取函数引发页错误的原因分析
读取FAT BPB的函数触发页错误,代码如下:
fat_bpb* fat_read_bpb(){ fat_bpb* bpb = kmalloc(sizeof(fat_bpb)); ide_read_sectors(DRIVE, 1, 6, (uint32)bpb); return bpb; }
单步调试发现错误出在ide_read_sectors(DRIVE, 1, 6, (uint32)bpb);这一行,同时GCC给出警告:
initialization of ‘fat_bpb *’ from ‘uint32’ {aka ‘unsigned int’} makes pointer from integer without a cast [-Wint-conversion]
当前使用的kmalloc实现如下:
extern uint32 end; uint32 placement_address = (uint32)&end; uint32 kmalloc_int(uint32 sz, int align, uint32 *phys) { // This will eventually call malloc() on the kernel heap. // For now, though, we just assign memory at placement_address // and increment it by sz. Even when we've coded our kernel // heap, this will be useful for use before the heap is initialised. if (align == 1 && (placement_address & 0xFFFFF000) ) { // Align the placement address; placement_address &= 0xFFFFF000; placement_address += 0x1000; } if (phys) { *phys = placement_address; } uint32 tmp = placement_address; placement_address += sz; return tmp; } uint32 kmalloc(uint32 sz) { return kmalloc_int(sz, 0, 0); }
核心原因:物理地址与虚拟地址混用
kmalloc返回的是物理地址
当前的kmalloc实现基于placement_address分配内存,而placement_address初始化为(uint32)&end——这里的&end是内核加载到物理内存中的结束地址,所以kmalloc返回的是物理内存地址,而非内核运行时使用的虚拟地址。指针误用物理地址导致未映射访问
在启用分页的系统中,内核代码使用的是虚拟地址空间,物理地址和虚拟地址属于完全不同的地址空间。你将kmalloc返回的物理地址直接赋值给fat_bpb*指针,相当于把物理地址当成虚拟地址去使用:- 当
ide_read_sectors尝试向该地址写入数据时,CPU会将其视为虚拟地址去查询页表,但这个物理地址对应的虚拟地址并未在页表中完成映射,直接触发页错误。 - GCC的警告已经明确提示了类型不匹配的问题,本质就是地址空间的错误混用。
- 当
IDE函数的参数要求加剧问题
如果ide_read_sectors的第四个参数需要的是虚拟地址,那么传入物理地址必然导致访问无效地址;如果它需要的是物理地址,那后续通过bpb指针访问内存时(同样把物理地址当虚拟地址用)也会触发页错误。而你单步调试发现错误出在ide_read_sectors调用行,说明函数内部在访问该地址时是按虚拟地址处理的。
解决方向
- 修改kmalloc实现,使其返回虚拟地址,确保分配的内存区域已经在页表中完成映射。
- 若IDE函数需要物理地址,可使用
kmalloc_int的phys参数同时获取物理地址和虚拟地址:用虚拟地址作为指针使用,物理地址传给IDE函数。 - 修复类型转换问题,避免直接将
uint32类型的地址赋值给指针,明确区分物理地址与虚拟地址的使用场景。
内容的提问来源于stack exchange,提问作者xingharvey
相关产品推荐
相关产品推荐

