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

无操作系统环境下运行C程序遇无限重启问题求助

问题分析与解决

你的代码存在几个致命问题,直接导致机器无限重启:

  • 错误使用Linux系统调用:int 0x80是Linux内核提供的系统调用接口,裸机环境没有操作系统,没有任何代码处理这个中断,触发后会导致CPU执行未定义操作,直接引发重启。
  • 数据定义侵入代码段:file_name db 'e.exe'直接放在mov eax,5之后,汇编器会把这段数据当作指令的一部分让CPU执行,完全打乱了代码流程,必然出错。数据应该放在代码段末尾、times指令之前的位置。
  • 裸机无文件系统支持:你试图打开e.exe,但裸机环境没有文件系统驱动(比如FAT、NTFS),也没有磁盘读取的逻辑,根本无法识别磁盘上的文件。要加载程序,你需要自己实现磁盘扇区读取的代码,把程序从磁盘的特定扇区读到内存中。
  • 读取参数完全无效:即使有文件系统,你填的ebx=1是标准输出的文件描述符,不是open返回的句柄;ecx=26也不是有效的内存缓冲区地址,这些参数完全错误。

修复思路

  1. 移除系统调用代码:裸机环境不能用int 0x80,需要自己实现磁盘读取逻辑。比如利用BIOS中断int 0x13(实模式下)或者直接操作磁盘控制器(保护模式下,需要设置I/O权限)。
  2. 正确组织代码与数据:把所有数据定义(比如file_name、BOOT_DISK)放在代码的最后部分,避免干扰指令执行。
  3. 实现磁盘扇区读取:如果你的C程序编译后放在MBR之后的连续扇区,比如第2扇区开始,你可以在实模式下用int 0x13读取这些扇区到内存(比如0x10000的位置),然后进入保护模式后跳转到该地址执行。
  4. 编译C程序为裸机可执行文件:C程序需要编译成没有依赖的裸机二进制,不能是Windows的.exe格式(.exe有PE头,裸机无法识别),要用交叉编译器编译成flat binary,比如:
    gcc -ffreestanding -m32 -c program.c -o program.o
    ld -m elf_i386 -Ttext 0x10000 --oformat binary program.o -o program.bin
    

修改后的示例代码(实模式读取扇区,再进保护模式)

[org 0x7c00]                        

mov [BOOT_DISK], dl                 

; 读取磁盘扇区:把第2扇区开始的2个扇区读到0x10000
mov ah, 0x02    ; 读扇区功能
mov al, 2       ; 读取2个扇区
mov ch, 0       ; 柱面0
mov cl, 2       ; 扇区2
mov dh, 0       ; 磁头0
mov dl, [BOOT_DISK]
mov bx, 0x1000  ; 缓冲区地址的高16位(es:bx)
mov es, bx
mov bx, 0x0     ; 低16位,最终地址是0x10000
int 0x13
jc disk_error   ; 如果读取失败,跳转到错误处理

; 进入保护模式
CODE_SEG equ GDT_code - GDT_start
DATA_SEG equ GDT_data - GDT_start

cli
lgdt [GDT_descriptor]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp CODE_SEG:start_protected_mode

disk_error:
mov ah, 0x0e
mov al, 'E'
int 0x10
jmp $
                                    
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 - 1
    dd GDT_start

[bits 32]
start_protected_mode:
    ; 初始化段寄存器
    mov ax, DATA_SEG
    mov ds, ax
    mov ss, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    ; 打印!
    mov al, '!'
    mov ah, 0x0f
    mov [0xb8000], ax

    ; 跳转到加载的C程序(地址0x10000)
    jmp 0x10000

    jmp $

BOOT_DISK: db 0

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

额外注意事项

  • 确保你的C程序没有使用任何标准库函数(比如printf),因为标准库依赖操作系统提供的服务,裸机环境无法支持。如果需要输出,可以直接操作VGA显存(0xb8000地址)。
  • 用交叉编译器编译C程序,避免生成依赖宿主系统的代码。
  • 把MBR和C程序的二进制文件写入磁盘镜像,比如用cat mbr.bin program.bin > disk.img,然后用QEMU等模拟器运行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 23:05:33