从零写OS(五十一):三个崩溃,三个修复

性能优化之后,跑 ls | grep foo 时发现三类崩溃,分别来自三个不同的地方。 崩溃一:内核栈溢出 ls | grep foo 在 busybox sh 里需要同时运行三个进程:sh、ls、grep,加上 fork 的中间状态,进程数量一下上来了。 运行时随机崩溃,串口输出乱码。通过 QEMU 调试发现是内核栈溢出——某个进程的内核栈被覆盖了。 原来内核栈只有 4KB: #define STACK_SIZE 4096 p->stack = (uint8_t *)pmm_alloc(); // 只有 1 个物理页 busybox 的调用链比较深(sh → fork → exec → elf_load → ext2_read → bcache_get → …),4KB 撑不住。 改成 8KB,用 kmalloc 分配(pmm_alloc 只能分配 4KB): #define STACK_SIZE 8192 p->stack = (uint8_t *)kmalloc(STACK_SIZE); 崩溃二:cd .. 跳到根目录 / # mkdir -p /usr/bin / # cd /usr/bin /usr/bin # cd .. / # ← 应该到 /usr,结果跳到 / 路径解析遇到 .. 时,原来直接跳回根目录: ...

June 4, 2026 · 1 min · 大飞

从零写OS(四十九):消灭 static 缓冲区

内核模块跑起来之后,发现 SMP 下偶发崩溃。追查下去,发现是一类隐藏很深的 bug:static 局部缓冲区。 static 局部变量在 SMP 下是定时炸弹 // ext2.c 里随处可见这样的代码 int ext2_read(uint32_t inum, ...) { static uint8_t blk[4096]; // 危险! // ... } static 局部变量存在 .bss 段里,整个内核只有一份。单核下没问题,但 SMP 下: CPU0 和 CPU1 同时读不同的文件 都调用 ext2_read,都在用同一个 blk[] 互相覆盖对方的数据 → 文件读出来是乱的 ext2.c 里有十几处这样的 static 缓冲区,vfs.c 里也有。全部改成 kmalloc: // 修改后 uint8_t *blk = (uint8_t *)kmalloc(block_size); if (!blk) return -1; // ... 使用 ... kfree(blk); 同时还有一个更隐蔽的问题:间接块的 LBA 缓存: // 修改前:有状态缓存,SMP 下竞争 static uint32_t ind1[1024]; static uint32_t ind1_bno = 0; // 上次读的 LBA,用来判断是否要重读 if (ind1_bno != inode.i_block[12]) { ind1_bno = inode.i_block[12]; read_block(ind1_bno, ind1); } 两个核心同时修改 ind1_bno,都以为自己读的数据有效——去掉缓存,每次都读: ...

June 4, 2026 · 1 min · 大飞
京ICP备14031575号-3