上下文(可能不需要):
作为一项学习练习,我正在尝试为 Raspberry Pi 实现一个迷你“操作系统”。
我目前正在实现一个非常愚蠢的内存管理系统。我已经启用了 MMU,并且正在获取可用的 kmalloc。
它已经可以从预先存在的小内核堆中分配内存块,映射在代码和数据段之后。我试图通过映射更多页面来让它根据需要增长。它还必须能够产生物理上连续的块。
该代码托管在Github上,有一个专门用于调试代码的分支。请注意,这不是一个组织良好、注释良好或非常聪明的代码的示例。:)
实际问题:
在尝试调试数据中止时,我发现了一些非常奇怪的东西。
这是我的 kmalloc 中的一段代码:
next->prev_size = chunk->size;
next->size = -1;
term_printf(term, "A chunk->next_free = 0x%x\n", chunk->next_free);
term_printf(term, "B chunk->next_free = 0x%x\n", chunk->next_free);
*prev_list = next;
next->next_free = chunk->next_free;
term_printf(term, "next_free = 0x%x, chunk 0x%x\n", next->next_free, chunk->next_free);
term_printf(term, "next_free = 0x%x, chunk 0x%x\n", next->next_free, chunk->next_free);
我跑了三遍。结果如下:
# 1st
A chunk->next_free = 0x0
B chunk->next_free = 0x0
next_free = 0x0, chunk 0x0
next_free = 0x0, chunk 0x0
# 2nd
A chunk->next_free = 0xffffffff
B chunk->next_free = 0x0
next_free = 0x0, chunk 0xffffffff
next_free = 0x0, chunk 0x0
# 3rd
A chunk->next_free = 0xffffffff
B chunk->next_free = 0xffffffff
next_free = 0xffffffff, chunk 0xffffffff
next_free = 0xffffffff, chunk 0xffffffff
第一次和第三次迭代看起来很正常(虽然 next_free 的值应该是 0,但数据中止是因为它有 0xffffffff)。但是我的代码在第二个期间在做什么?O_o 什么样的黑魔法可以让我的 printf 在连续读取四次时为 chunk->next_free 输出两个不同的值?o_o
数据对齐良好,页面可缓存且不可缓冲(使它们不可缓存无济于事),无论编译器优化是打开还是关闭,我都会得到相同的结果。我尝试在那里设置数据内存屏障,但实际上它什么也没做。我还检查了生产的组件,它看起来没问题。
我认为这可能是由损坏的 TLB 引起的。我在每个新页面映射后发出“使统一单一条目无效”(mcr p15, 0, %[addr], c8, c7, 1)。够了吗?
我尝试使用 qemu 进行调试,但在设置已用物理页面的位图时,它会提前中止数据,尽管这部分在 Pi 上运行良好。
我只是在寻找可能导致这种行为的线索。如果您需要更多上下文,请询问,尽管我的代码目前正在迅速变化并且由于大量 printf 而变得混乱。
ETA:前两个 printf 使用 -O0 的反汇编:
c00025e4: e51b3018 ldr r3, [fp, #-24]
c00025e8: e5933008 ldr r3, [r3, #8]
c00025ec: e59b0004 ldr r0, [fp, #4]
c00025f0: e59f10a0 ldr r1, [pc, #160] ; c0002698 <kmalloc_wilderness+0x2c0>
c00025f4: e1a02003 mov r2, r3
c00025f8: eb000238 bl c0002ee0 <term_printf>
c00025fc: e51b3018 ldr r3, [fp, #-24]
c0002600: e5933008 ldr r3, [r3, #8]
c0002604: e59b0004 ldr r0, [fp, #4]
c0002608: e59f1088 ldr r1, [pc, #140] ; c000269c <kmalloc_wilderness+0x2c4>
c000260c: e1a02003 mov r2, r3
c0002610: eb000232 bl c0002ee0 <term_printf>
所以它把chunk
r3 的地址放进去,然后执行 aldr
来获取next_free
。它在第二个prinf之前再次执行此操作。只有一个内核,DMA 没有运行,所以在调用之间没有改变内存中的值。
使用 -O2:
c0001c38: e1a00006 mov r0, r6
c0001c3c: e59f10d8 ldr r1, [pc, #216] ; c0001d1c <kmalloc_wilderness+0x1b8>
c0001c40: e5942008 ldr r2, [r4, #8]
c0001c44: eb000278 bl c000262c <term_printf>
c0001c48: e1a00006 mov r0, r6
c0001c4c: e59f10cc ldr r1, [pc, #204] ; c0001d20 <kmalloc_wilderness+0x1bc>
c0001c50: e5942008 ldr r2, [r4, #8]
c0001c54: eb000274 bl c000262c <term_printf>
所以它仍然用ldr
. 这就是为什么我在两个优化级别都得到相同的结果。
新编辑:我添加了更多 printfs,似乎奇点发生在这一点上:
next->size = -1;
在这行之后,chunk->next_free 变成了海森堡的猫。之前,它读为 0。
结构定义如下:
struct kheap_chunk {
size_t prev_size;
size_t size; // -1 for wilderness chunk, bit 0 high if free
struct kheap_chunk *next_free;
};
chunk
并且next
不要重叠。
如果我将“奇点线”移到 下方next->next_free = chunk->next_free
,它会停止在两个值之间交替,但这仍然很奇怪: chunk->next_free 之前为 0 *prev_list = next
,之后为 0xffffffff 。但是 next->next_free 仍然设置为 0。