1

上下文(可能不需要):

作为一项学习练习,我正在尝试为 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>

所以它把chunkr3 的地址放进去,然后执行 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。

4

0 回答 0