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

2024-11-04 08:43:00
admin
原创
34
摘要:问题描述:我在 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 语言。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用