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

QEMU从汇编加载C代码时闪烁重启问题求助

QEMU重启且GDB无法连接,跳转到内核地址触发问题的排查与解决

首先解决GDB连接问题,没有调试环境根本没法定位问题:

  • 启动QEMU时必须加上调试参数,让它开放调试端口并暂停等待连接:
    qemu-system-i386 -s -S -fda your_os_image.img
    
    -s 等价于 -gdb tcp::1234,开放1234端口供GDB连接;-S 让QEMU启动后暂停,直到GDB发送继续指令。
  • 打开另一个终端启动GDB,连接到QEMU:
    gdb your_kernel.elf
    (gdb) target remote localhost:1234
    (gdb) continue
    
    这样就能正常调试,后续可以在jmp KERNELLOC前加断点,查看0x1000地址的内容是否正确。

接下来是核心问题:你的引导扇区磁盘加载逻辑有致命错误
你用int 0x13加载2个扇区,但既没指定加载目标地址,也没设置磁盘参数,更没检查加载是否成功,导致内核根本没被加载到0x1000,跳过去执行的是随机垃圾代码,直接触发崩溃重启。修改引导扇区的磁盘加载部分:

[org 0x7c00]

KERNELLOC equ 0x1000
CODESEG equ gdt_code - gdt_start
DATASEG equ gdt_data - gdt_start

; Load Sectors - 修正后的磁盘加载逻辑
mov ah, 0x02        ; BIOS读磁盘功能号
mov al, 0x2         ; 读取2个扇区
mov ch, 0x00        ; 柱面号0
mov dh, 0x00        ; 磁头号0
mov cl, 0x02        ; 起始扇区号2(引导扇区是第1个扇区)
mov bx, KERNELLOC   ; 加载到内存地址0x1000
int 0x13            ; 调用BIOS中断
jc disk_error       ; 如果CF标志位被置位,说明加载失败

; 原有代码继续
cli
lgdt [gdt_descriptor]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp CODESEG:start_protected_mode

; 新增磁盘错误处理
disk_error:
    ; 这里需要你实现一个16位模式下的字符串打印函数print_string_16
    mov bx, DISK_ERROR_MSG
    call print_string_16
    jmp $ ; 死循环,避免继续执行

DISK_ERROR_MSG db "Disk read failed!", 0

; 以下是原有GDT和保护模式代码,保持不变
gdt_start:
    gdt_null:
        dd 0x0
        dd 0x0

    gdt_code:
        dw 0xffff
        dw 0x0
        db 0x0
        db 0b10011010
        db 0b11001111
        db 0x0
    
    gdt_data:
        dw 0xffff
        dw 0x0
        db 0x0
        db 0b10010010
        db 0b11001111
        db 0x0
gdt_end:

gdt_descriptor:
    dw gdt_end - gdt_start
    dd gdt_start

[bits 32]
start_protected_mode:
    mov ax, DATASEG
    mov ss, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    mov ebp, 0x90000
    mov esp, ebp

jmp KERNELLOC

times 510-($-$$) db 0
dw 0xaa55

另外需要确认内核的链接地址是否正确:

  • 你的C内核必须通过链接脚本指定加载地址为0x1000,比如创建linker.ld:
    ENTRY(kernel_main)
    SECTIONS {
        . = 0x1000;
        .text : { *(.text) }
        .data : { *(.data) }
        .bss : { *(.bss) }
    }
    
  • 编译链接内核时使用:
    gcc -m32 -ffreestanding -c kernel.c -o kernel.o
    ld -m elf_i386 -T linker.ld kernel.o -o kernel.bin
    
  • 最后合并引导扇区和内核生成镜像:
    cat boot.bin kernel.bin > os.img
    

做完这些修改后,再用QEMU启动,应该能正常加载内核,不会出现重启问题。如果还有异常,用GDB在jmp KERNELLOC处断点,查看0x1000地址的内容是否是内核的机器码,进一步排查。

内容的提问来源于stack exchange,提问作者SKZI

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 12:04:51