上下文(可能不需要):
作为一项学习练习,我正在尝试为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-> gt; next_free输出两个不同的值? O_O
数据排列良好,页面可缓存且不可缓冲(使它们不可缓存无效),无论编译器优化是打开还是关闭,我都会得到相同的结果。我尝试在那里抛出一个数据内存屏障,但实际上它没有做任何事情。我还检查了生产的组件,看起来还不错。
我认为这可能是由损坏的TLB引起的。我在每次新页面映射后发出“Invalidate unified single entry”(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,然后执行ldr
以获取next_free
。它在第二个原则之前再次出现。只有一个核心,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-&gt; 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-&gt; next_free在*prev_list = next
之前为0,之后为0xffffffff。但是next-&gt; next_free仍然设置为0。