如何在 ubuntu 下使用 nasm(汇编)从键盘读取单个字符输入?

2024-11-04 08:43:00
admin
原创
146
摘要:问题描述:我在 ubuntu 下使用 nasm。顺便说一下,我需要从用户的键盘获取单个输入字符(例如当程序要求您输入 y/n ? 时),因此当按下键而不按回车键时,我需要读取输入的字符。我在 Google 上搜索了很多,但我找到的所有内容都与此行 ( int 21h) 有关,导致“分段错误”。请帮我弄清楚如何...

问题描述:

我在 ubuntu 下使用 nasm。顺便说一下,我需要从用户的键盘获取单个输入字符(例如当程序要求您输入 y/n ? 时),因此当按下键而不按回车键时,我需要读取输入的字符。我在 Google 上搜索了很多,但我找到的所有内容都与此行 ( int 21h) 有关,导致“分段错误”。请帮我弄清楚如何获取单个字符或如何克服这个分段错误。


解决方案 1:

可以用汇编语言来实现,但并不容易。您不能使用 int 21h,这是 DOS 系统调用,在 Linux 下不可用。

要在类 UNIX 操作系统(如 Linux)下从终端获取字符,您需要从 STDIN(文件编号 0)读取。通常,read 系统调用将阻塞,直到用户按下 Enter 键。这称为规范模式。要读取单个字符而不等待用户按下 Enter 键,您必须先禁用规范模式。当然,如果您稍后想要行输入,则必须在程序退出之前重新启用它。

要在 Linux 上禁用规范模式,可以使用 ioctl 系统调用将 IOCTL(IO 控制)发送到 STDIN。我假设您知道如何从汇编程序进行 Linux 系统调用。

ioctl 系统调用有三个参数。第一个是发送命令的文件 (STDIN),第二个是 IOCTL 编号,第三个通常是指向数据结构的指针。ioctl 在成功时返回 0,在失败时返回负错误代码。

您需要的第一个 IOCTL 是 TCGETS(编号 0x5401),它以 termios 结构的形式获取当前终端参数。第三个参数是指向 termios 结构的指针。从内核源代码来看,termios 结构定义如下:

struct termios {
    tcflag_t c_iflag;               /* input mode flags */
    tcflag_t c_oflag;               /* output mode flags */
    tcflag_t c_cflag;               /* control mode flags */
    tcflag_t c_lflag;               /* local mode flags */
    cc_t c_line;                    /* line discipline */
    cc_t c_cc[NCCS];                /* control characters */
};

其中 tcflag_t 长 32 位,cc_t 长 1 字节,NCCS 当前定义为 19。请参阅 NASM 手册以了解如何方便地定义和保留此类结构的空间。

因此,一旦您获得了当前的 termios,就需要清除规范标志。此标志位于 c_lflag 字段中,掩码为 ICANON (0x00000002)。要清除它,请计算 c_lflag AND (NOT ICANON)。并将结果存储回 c_lflag 字段。

现在您需要通知内核您对 termios 结构的更改。使用 TCSETS (编号 0x5402) ioctl,将第三个参数设置为您的 termios 结构的地址。

如果一切顺利,终端现在处于非规范模式。您可以通过设置规范标志(通过将 c_lflag 与 ICANON 进行 ORing)并再次调用 TCSETS ioctl 来恢复规范模式。在退出之前始终恢复规范模式

正如我所说,这并不容易。

解决方案 2:

我最近需要这样做,并且受到 Callum 的出色回答的启发,我写了以下内容(适用于 x86-64 的 NASM):

DEFAULT REL

section .bss
termios:        resb 36

stdin_fd:       equ 0           ; STDIN_FILENO
ICANON:         equ 1<<1
ECHO:           equ 1<<3

section .text
canonical_off:
        call read_stdin_termios

        ; clear canonical bit in local mode flags
        and dword [termios+12], ~ICANON

        call write_stdin_termios
        ret

echo_off:
        call read_stdin_termios

        ; clear echo bit in local mode flags
        and dword [termios+12], ~ECHO

        call write_stdin_termios
        ret

canonical_on:
        call read_stdin_termios

        ; set canonical bit in local mode flags
        or dword [termios+12], ICANON

        call write_stdin_termios
        ret

echo_on:
        call read_stdin_termios

        ; set echo bit in local mode flags
        or dword [termios+12], ECHO

        call write_stdin_termios
        ret

; clobbers RAX, RCX, RDX, R8..11 (by int 0x80 in 64-bit mode)
; allowed by x86-64 System V calling convention    
read_stdin_termios:
        push rbx

        mov eax, 36h
        mov ebx, stdin_fd
        mov ecx, 5401h
        mov edx, termios
        int 80h            ; ioctl(0, 0x5401, termios)

        pop rbx
        ret

write_stdin_termios:
        push rbx

        mov eax, 36h
        mov ebx, stdin_fd
        mov ecx, 5402h
        mov edx, termios
        int 80h            ; ioctl(0, 0x5402, termios)

        pop rbx
        ret

(编者注:不要int 0x80在 64 位代码中使用:如果在 64 位代码中使用 32 位 int 0x80 Linux ABI 会发生什么? - 它会在 PIE 可执行文件中中断(其中静态地址不在低 32 位),或者在堆栈上使用 termios 缓冲区。它实际上在传统的非 PIE 可执行文件中工作,并且此版本可以轻松移植到 32 位模式。)

然后你可以执行以下操作:

call canonical_off

如果您正在阅读一行文本,您可能还想执行以下操作:

call echo_off

这样每个字符在输入时就不会被回显。

可能有更好的方法可以做到这一点,但它在 64 位 Fedora 安装上对我来说是有效的。

您可以在手册页termios(3)termbits.h源代码中找到更多信息。

解决方案 3:

简单的方法:对于文本模式程序,使用libncurses来访问键盘;对于图形程序,使用Gtk+。

困难的方式:假设一个文本模式的程序,你必须告诉内核你想要单字符输入,然后你必须做大量的记录和解码。这真的很复杂。没有与旧的 DOS 例程相当的东西getch()。你可以从这里开始学习如何做到这一点:终端 I/O。图形程序甚至更复杂;最低级别的 API 是Xlib。

无论哪种方式,您都会疯狂地用汇编语言编写代码;请改用 C 语言。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1124  
  IPD(Integrated Product Development,集成产品开发)流程是一种广泛应用于高科技和制造业的产品开发方法论。它通过跨职能团队的紧密协作,将产品开发周期缩短,同时提高产品质量和市场成功率。在IPD流程中,CDCP(Concept Decision Checkpoint,概念决策检查点)是一个关...
IPD培训课程   79  
  研发IPD(集成产品开发)流程作为一种系统化的产品开发方法,已经在许多行业中得到广泛应用。它不仅能够提升产品开发的效率和质量,还能够通过优化流程和资源分配,显著提高客户满意度。客户满意度是企业长期成功的关键因素之一,而IPD流程通过其独特的结构和机制,能够确保产品从概念到市场交付的每个环节都围绕客户需求展开。本文将深入...
IPD流程   70  
  IPD(Integrated Product Development,集成产品开发)流程是一种以跨职能团队协作为核心的产品开发方法,旨在通过优化资源分配、提高沟通效率以及减少返工,从而缩短项目周期并提升产品质量。随着企业对产品上市速度的要求越来越高,IPD流程的应用价值愈发凸显。通过整合产品开发过程中的各个环节,IPD...
IPD项目管理咨询   82  
  跨部门沟通是企业运营中不可或缺的一环,尤其在复杂的产品开发过程中,不同部门之间的协作效率直接影响项目的成败。集成产品开发(IPD)作为一种系统化的项目管理方法,旨在通过优化流程和增强团队协作来提升产品开发的效率和质量。然而,跨部门沟通的复杂性往往成为IPD实施中的一大挑战。部门之间的目标差异、信息不对称以及沟通渠道不畅...
IPD是什么意思   74  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用