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

为何无法在x86汇编中打印winsize_struct缓冲区内容?

32位x86汇编获取终端尺寸失败问题解决及行列打印方法

核心问题分析

你遇到的问题主要来自两个关键点:

  • sys_write的使用误区:直接打印winsize_struct的二进制内容会输出乱码,因为sys_write仅能输出ASCII字符流,而缓冲区里是16位整数数据,必须先转成ASCII字符串才能打印。
  • ioctl调用的潜在问题:虽然你调整了参数,但可能忽略了终端文件描述符的有效性(比如程序被重定向时,stdin/stdout可能不是终端),以及未对系统调用返回值做错误检查。

此外,你尝试的几种方法中:

  • lea edx, [winsize_struct]是正确的取地址方式,mov edx, winsize_struct在多数情况下也能生效,但取决于段定义;
  • ebx设为0或1本身没问题,但需确保对应的文件描述符指向终端设备;
  • winsize_struct放在.bss段是合理的,避免初始化数据的浪费。

完整解决方案代码

以下是可直接编译运行的32位NASM代码,包含终端尺寸获取、最小尺寸检查、数值转ASCII打印的完整逻辑:

section .data
    min_rows equ 24
    min_cols equ 80
    err_msg db "终端尺寸过小,请调整后重试!", 0xA
    err_len equ $ - err_msg
    newline db 0xA
    dev_tty db "/dev/tty", 0  ; 直接指向当前终端,避免重定向影响

section .bss
    winsize_struct resb 8  ; winsize结构:4个16位字段,共8字节

section .text
    global _start

_start:
    ; 打开/dev/tty,确保获取当前终端的文件描述符
    mov eax, 5          ; sys_open 系统调用号
    mov ebx, dev_tty
    mov ecx, 0          ; O_RDONLY 只读模式
    int 0x80
    cmp eax, -1
    je ioctl_failed     ; 打开失败则跳转至错误处理
    mov ebx, eax        ; 将有效的终端fd存入ebx

    ; 调用ioctl获取终端尺寸
    mov eax, 54          ; sys_ioctl 系统调用号
    mov ecx, 0x5413      ; TIOCGWINSZ 请求码
    lea edx, [winsize_struct]
    int 0x80

    ; 关闭/dev/tty的文件描述符
    push eax            ; 保存ioctl的返回值
    mov eax, 6          ; sys_close 系统调用号
    int 0x80
    pop eax

    ; 检查ioctl调用是否成功
    cmp eax, -1
    je ioctl_failed

    ; 读取终端行列数:ws_row(行数)在偏移0,ws_col(列数)在偏移2
    mov ax, [winsize_struct]     ; 行数
    mov bx, [winsize_struct+2]   ; 列数

    ; 检查是否满足最小尺寸要求
    cmp ax, min_rows
    jl size_too_small
    cmp bx, min_cols
    jl size_too_small

    ; 打印行数
    push ebx
    mov eax, ax
    call print_num
    call print_newline
    pop ebx

    ; 打印列数
    mov eax, bx
    call print_num
    call print_newline

    ; 正常退出程序
    mov eax, 1
    xor ebx, ebx
    int 0x80

ioctl_failed:
    ; 打印ioctl失败提示
    mov eax, 4
    mov ebx, 1
    mov ecx, err_msg
    mov edx, err_len
    int 0x80
    mov eax, 1
    mov ebx, 1
    int 0x80

size_too_small:
    ; 打印尺寸过小提示
    mov eax, 4
    mov ebx, 1
    mov ecx, err_msg
    mov edx, err_len
    int 0x80
    mov eax, 1
    mov ebx, 1
    int 0x80

; 辅助函数:将eax中的无符号整数打印为ASCII字符串
print_num:
    push ebx
    push ecx
    push edx
    push esi
    mov esi, 10         ; 十进制除数
    xor ecx, ecx        ; 计数栈中数字的个数

num_loop:
    xor edx, edx
    div esi             ; eax = eax/10,edx = eax%10
    push edx            ; 将余数压栈
    inc ecx
    test eax, eax       ; 检查商是否为0
    jnz num_loop

print_loop:
    pop edx
    add dl, '0'         ; 将数字转为ASCII字符
    mov [esp], dl       ; 利用栈临时存储字符(避免额外内存)
    mov eax, 4
    mov ebx, 1
    mov ecx, esp
    mov edx, 1
    int 0x80
    dec ecx
    jnz print_loop

    pop esi
    pop edx
    pop ecx
    pop ebx
    ret

; 辅助函数:打印换行符
print_newline:
    mov eax, 4
    mov ebx, 1
    mov ecx, newline
    mov edx, 1
    int 0x80
    ret

编译运行命令

nasm -f elf32 your_program.asm -o your_program.o
ld -m elf_i386 your_program.o -o your_program
./your_program

关键细节说明

  1. 终端文件描述符的可靠性:使用/dev/tty而非stdin/stdout,确保即使程序输入输出被重定向,仍能获取当前终端的尺寸。
  2. 数值转ASCII逻辑:通过除法取余将整数拆分为单个数字,转成ASCII后逐个打印,这是汇编中打印数字的标准方法。
  3. 系统调用错误检查:每次系统调用后都检查返回值,能快速定位打开终端、ioctl调用等环节的失败原因。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 15:26:23