为何无法在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
关键细节说明
- 终端文件描述符的可靠性:使用
/dev/tty而非stdin/stdout,确保即使程序输入输出被重定向,仍能获取当前终端的尺寸。 - 数值转ASCII逻辑:通过除法取余将整数拆分为单个数字,转成ASCII后逐个打印,这是汇编中打印数字的标准方法。
- 系统调用错误检查:每次系统调用后都检查返回值,能快速定位打开终端、ioctl调用等环节的失败原因。
内容的提问来源于stack exchange,提问作者HerrWeishaupt
相关产品推荐
相关产品推荐

