16位x86汇编内核调用输入函数后卡顿问题求助
自研OS字节码解释器输入后卡顿问题排查
问题描述
出于兴趣自研操作系统,参考JVM设计实现了内核级字节码解释语言,添加基础I/O功能后编写了一个询问用户名并输出问候的程序,但输入姓名后代码卡顿,光标无响应,修改相关函数后问题仍存在。
编译与运行环境
- 编译:使用NASM编译
boot.asm和kern.asm,命令:nasm -f bin filename.asm -o filename.bin - 生成镜像:用
dd命令生成单文件镜像:dd if=filename.bin of=filename.img,再合并为最终镜像:cat boot.img kern.img > main.img - 运行:在QEMU中加载
main.img运行
相关代码
boot.asm
[org 0x7c00] [bits 16] xor ax, ax mov ds, ax mov es, ax mov ss, ax ; Set up the stack. mov ss, ax mov sp, 0x7c00 mov ah, 0x01 mov cx, 0x0007 int 0x10 mov si, ntgos_bootld_msg call out mov si, bootld_started_msg call out mov si, disk_reading_msg call out ; Read two sectors from the disk. mov si, KERN_ADDR call disk ; Handle disk reading error. jc disk_error mov si, disk_done_msg call out mov si, kern_starting_msg call out ; Jump to kernel. jmp KERN_ADDR jmp $ disk_error: mov si, disk_error_msg call out jmp $ %include "boot-libs/out.asm" %include "boot-libs/disk.asm" ntgos_bootld_msg: db "ENTEEG OS BOOTLOADER", 0x0a, 0x0d, 0x00 bootld_started_msg: db " Started.", 0x0a, 0x0d, 0x00 disk_reading_msg: db " Reading and loading disk at address 0x7e00...", 0x0a, 0x0d, 0x00 disk_done_msg: db " Disk reading done successfully.", 0x0a, 0x0d, 0x00 disk_error_msg: db " (!) ERROR! Error reading disk. Cannot continue booting. Please restart the computer.", 0x0a, 0x0d, 0x00 kern_starting_msg: db " Starting kernel...", 0x0a, 0x0d, 0x00 KERN_ADDR equ 0x7e00 times 510-($-$$) db 0x00 dw 0xaa55
boot-libs/out.asm
out: pusha mov ah, 0x0e out_loop: mov al, [si] cmp al, 0x00 je out_end int 0x10 inc si jmp out_loop out_end: popa ret
boot-libs/disk.asm
disk: pusha mov ah, 0x02 disk_loop: mov al, 0x02 mov ch, 0x00 mov dh, 0x00 mov cl, 0x02 mov bx, 0x0000 mov es, bx mov bx, si int 0x13 jnc disk_end disk_err_: popa stc ret disk_end: popa ret
kern.asm
[org 0x7e00] [bits 16] mov si, ntgos_kern_msg call out mov si, kern_started_msg call out ; Clear the screen. mov ah, 0x00 mov al, 0x03 int 0x10 mov si, code call exec jmp $ %include "kern-libs/ioutils.asm" %include "exec.asm" ntgos_kern_msg: db "ENTEEG OS KERNEL", 0x0a, 0x0d, 0x00 kern_started_msg: db " Started.", 0x0a, 0x0d, 0x00 INPUT_ADDR equ 0xd000 code: db 0x25, "Type in your name:", 0x0a, 0x0d, 0x00, 0x27, 0x25, "Hi, ", 0x00, 0x26, 0xd0, 0x00, 0x00 times 1024-($-$$) db 0x00
exec.asm
exec: mov al, [si] cmp al, 0x25 ; Out je _0x25 cmp al, 0x26 ; Outaddr je _0x26 cmp al, 0x27 ; In je _0x27 cmp al, 0x00 ; Exit program je _0x00 jmp exec _0x25: call print_out_prompt inc si call out jmp exec_ret _0x26: call print_out_prompt inc si call outaddr jmp exec_ret _0x27: pusha mov si, in_prompt call out popa call in jmp exec_ret _0x00: ret exec_ret: inc si jmp exec print_out_prompt: pusha mov si, out_prompt call out popa ret in_prompt: db "{?} ", 0x00 out_prompt: db "{.} ", 0x00
kern-libs/ioutils.asm
out: mov ah, 0x0e out_loop: mov al, [si] cmp al, 0x00 je out_end int 0x10 inc si jmp out_loop out_end: ret outaddr: mov ah, 0x0e mov bl, [si] inc si mov bh, [si] outaddr_loop: mov al, [bx] cmp al, 0x00 je outaddr_end int 0x10 inc bx jmp outaddr_loop outaddr_end: ret in: mov cx, 0x004c in_loop: mov ah, 0x00 int 0x16 mov ah, 0x0e int 0x10 cmp cx, 0x0000 je in_full_buff cmp al, 0x0d je in_end mov [si], al inc si dec cx jmp in_loop in_full_buff: stc in_end: mov byte [si], 0x00 mov al, 0x0a int 0x10 ret
问题根源与修复
1. 输入函数in的SI寄存器未初始化
在exec.asm的_0x27分支中,调用in函数前未指定输入数据的存储地址,此时si指向字节码流中的指令位置,输入数据会覆盖字节码,导致后续解释逻辑混乱。
修复:调用in前将si设置为预定义的INPUT_ADDR:
_0x27: pusha mov si, in_prompt call out popa mov si, INPUT_ADDR ; 指定输入存储地址 call in jmp exec_ret
2. outaddr函数地址解析字节顺序错误
字节码中0x26指令后的地址是0xd0, 0x00,对应0x00d0,但INPUT_ADDR是0xd000,同时outaddr先读低字节再读高字节,导致地址解析错误。
修复:调整字节码中的地址顺序为0x00, 0xd0:
code: db 0x25, "Type in your name:", 0x0a, 0x0d, 0x00, 0x27, 0x25, "Hi, ", 0x00, 0x26, 0x00, 0xd0, 0x00
3. in函数缓冲区满时无退出逻辑
当输入缓冲区满(cx减为0)时,in_full_buff仅设置进位标志,未跳转到结束逻辑,会继续执行越界写入操作。
修复:添加跳转指令到结束逻辑:
in_full_buff: stc jmp in_end ; 跳转到输入结束处理
4. 内核out函数未保存寄存器
内核的out函数未保存si等寄存器,调用后会破坏字节码解释器的si指针,导致执行流程中断。
修复:给out函数添加寄存器保存与恢复:
out: pusha ; 保存寄存器 mov ah, 0x0e out_loop: mov al, [si] cmp al, 0x00 je out_end int 0x10 inc si jmp out_loop out_end: popa ; 恢复寄存器 ret
验证修复
应用上述修改后,重新编译生成镜像并运行,输入用户名按回车后将正常输出问候语,不再出现卡顿。
内容的提问来源于stack exchange,提问作者varevind
相关产品推荐
相关产品推荐

