You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义操作系统内核消息无法显示:引导加载器还是内核问题?

自定义操作系统内核消息无法显示:引导加载器还是内核问题?

看起来你在从零构建OS的过程中卡到了最关键的引导跳转环节——引导加载器的消息能正常输出,但内核的问候就是不出来,这种底层问题确实让人头大,我帮你梳理下核心问题点和修复方向:

一、最可能的元凶:引导加载器的LBA转CHS计算错误

你怀疑磁盘读取逻辑有问题,这个方向完全正确,而且你的LBA_to_CHS函数里存在明显的计算逻辑错误,导致磁盘读取的是错误扇区,内核根本没被正确加载到内存里:

错误的核心点

标准的LBA转CHS公式是:

  • 扇区 = (LBA % 每磁道扇区数) + 1(扇区从1开始计数)
  • 磁头 = (LBA / 每磁道扇区数) % 磁头数
  • 柱面 = (LBA / 每磁道扇区数) / 磁头数

而你的代码里把除法和乘法搞反了,还遗漏了CL寄存器的格式要求(BIOS的int 13h要求CL低6位是扇区,高2位是柱面的高两位),直接导致计算出的CHS地址完全错误,读不到内核所在的扇区。

修复后的LBA_to_CHS函数

替换成下面的正确实现:

; Arguments:
;     si - LBA address
; Returns:
;     ch, dh, cl - CHS address (符合BIOS int13h要求的格式)
LBA_to_CHS:
    push ax
    push bx
    push dx

    mov ax, si                  ; AX = LBA地址
    xor dx, dx
    div word [_sectors_per_track] ; AX = LBA / 每磁道扇区数, DX = LBA % 每磁道扇区数
    inc dx                      ; 扇区从1开始,所以余数+1
    mov cl, dl                  ; CL暂存扇区号(低6位)
    
    xor dx, dx
    div word [_head_count]      ; AX = 柱面数, DX = 磁头号
    mov dh, dl                  ; DH = 磁头号
    mov ch, al                  ; CH = 柱面数低8位
    shl ah, 6                   ; 柱面数高2位移到CL的高两位
    or cl, ah                   ; CL = 扇区号(低6位) | 柱面高2位(高2位)

    pop dx
    pop bx
    pop ax
    ret

二、磁盘读取的辅助优化:稳妥保存驱动器号

BIOS启动引导加载器时,会把启动盘号存在dl寄存器里,你的代码尝试通过栈保存dl,但逻辑有点绕,不如直接把它存在变量里更稳妥:

  1. 在bootloader.asm的变量区添加:
_drive_number: db 0
  1. 在main函数开头保存驱动器号:
main:
mov ax, 0 
mov ds, ax
mov ss, ax
mov sp, [_stack_ptr_addr]
mov [_drive_number], dl ; 保存BIOS传递的启动盘号
; ... 其他代码
  1. 在read_disk函数调用int 13h前显式设置dl:
read_disk:
push ax
push si
mov si, 4
.retry:
dec si
cmp si, 0
je .read_error
mov ah, 02h
mov al, byte [_kernel_size] ; 显式用byte,避免16位变量的高8位干扰
mov dl, [_drive_number]     ; 加载保存的驱动器号
int 13h
jc .retry
; ... 其他代码

三、内核代码的小完善:保存寄存器

你的内核puts函数没有保存寄存器,虽然不是当前问题的核心,但会破坏调用者的寄存器状态,养成好习惯:

puts:
    push ax
    push si
    mov ah, 0x0E
.begin:
    lodsb
    cmp al, 0
    je .done
    int 10h
    jmp .begin
.done:
    pop si
    pop ax
    ret

四、调试验证步骤

如果修改后还是有问题,可以用这些方法定位:

  1. 添加调试消息:在read_disk成功后、跳转内核前,添加一个“内核已加载”的消息,如果这个消息能显示,说明磁盘读取成功,问题出在内核;如果不能,说明磁盘读取逻辑还有问题。
  2. 用QEMU调试内存:启动QEMU时加上-s -S参数,然后用GDB连接查看0x7E00地址的内容:
    # 启动QEMU并暂停
    qemu-system-i386 -fda build/dev.img -s -S
    # 另开终端启动GDB
    gdb
    target remote localhost:1234
    x/16x 0x7e00  # 查看0x7E00处的内存,应该和kernel.bin的内容一致
    
    如果内存内容和kernel.bin不一致,说明磁盘读取还是没成功;如果一致,再单步调试内核代码看哪里出问题。

备注:内容来源于stack exchange,提问作者Viliam Holly

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.21 15:49:35