从零写OS(五十三):进程退出时的资源回收

跑了一段时间之后,内核会越来越慢,最终卡死——不断创建进程、运行、退出,但内存一直在增长。原因是 proc_exit 什么都没回收。 proc_exit 原来做了什么 void proc_exit(int code) { current->exit_code = code; current->state = PROC_ZOMBIE; schedule(); // 完事了 } 进程用到的所有资源:fd、内核栈、用户页表、用户物理页——全部泄漏。 补全资源回收 void proc_exit(int code) { // 1. 关闭所有 fd for (int i = 0; i < PROC_MAX_FD; i++) { if (current->fd_table[i] >= 0) { vfs_close(current->fd_table[i]); current->fd_table[i] = -1; } } // 2. 释放内核栈 kfree(current->stack); current->stack = NULL; // 3. 释放用户页表和所有用户物理页 if (current->pml4 != kernel_pml4) { vmm_switch(kernel_pml4); // 先切回内核页表 vmm_free_user_pages(current->pml4); // 再释放 current->pml4 = NULL; } current->exit_code = code; current->state = PROC_ZOMBIE; schedule(); } vmm_free_user_pages:遍历四级页表释放 void vmm_free_user_pages(uint64_t *pml4) { for (int i4 = 0; i4 < 256; i4++) { // 只看低 256 项(用户空间) if (!(pml4[i4] & PAGE_PRESENT)) continue; uint64_t *pdpt = ENTRY_ADDR(pml4[i4]); for (int i3 = 0; i3 < 512; i3++) { // 跳过大页(内核 1GB 映射) uint64_t *pd = ENTRY_ADDR(pdpt[i3]); for (int i2 = 0; i2 < 512; i2++) { uint64_t *pt = ENTRY_ADDR(pd[i2]); for (int i1 = 0; i1 < 512; i1++) { if (pt[i1] & PAGE_USER) pmm_free(ENTRY_ADDR(pt[i1])); // 释放用户物理页 } pmm_free(pt); // 释放 PT 页 } pmm_free(pd); } pmm_free(pdpt); pml4[i4] = 0; } pmm_free(pml4); } 只遍历低 256 项对应的用户地址空间,高 256 项是内核映射(共享 kernel_pml4),不能释放。 ...

June 4, 2026 · 2 min · 大飞

从零写OS(三十二):启动 busybox sh —— 用户指针与内核页表的陷阱

ch31 已经能跑 musl libc 的 hello world 了,这一章目标更高:启动 busybox sh,看到 / # 提示符。busybox 是个真正的程序,碰到的问题也更真实。 准备工作 获取静态编译的 busybox wget https://busybox.net/downloads/binaries/1.35.0-x86_64-linux-musl/busybox chmod +x busybox file busybox # busybox: ELF 64-bit LSB executable, x86-64, statically linked 更新 ext2 镜像 busybox 需要 /bin/sh、/etc/passwd、/etc/group: sudo mkdir -p /tmp/ext2mnt/bin sudo mkdir -p /tmp/ext2mnt/etc sudo cp $(BUSYBOX) /tmp/ext2mnt/bin/busybox sudo cp $(BUSYBOX) /tmp/ext2mnt/bin/sh printf 'root:x:0:0:root:/root:/bin/sh\n' | sudo tee /tmp/ext2mnt/etc/passwd > /dev/null printf 'root:x:0:\n' | sudo tee /tmp/ext2mnt/etc/group > /dev/null Bug 1:GDT TSS 描述符溢出 x86_64 下 TSS 描述符是 16 字节(两个 qword),GDT 必须为它预留两个连续槽位。之前只预留了一个,导致 TSS 描述符的高 8 字节覆盖了相邻的全局变量(恰好是 mmap_next),进程一分配匿名内存就跳到奇怪的地址。 ...

May 22, 2026 · 2 min · 大飞
京ICP备14031575号-3