【问题标题】:How to 'silently' get user input from a terminal on Linux?如何“静默”地从 Linux 上的终端获取用户输入?
【发布时间】:2019-11-09 13:47:20
【问题描述】:

我需要让用户输入跳过其中的每个辅音。所以每个第二个辅音应该被打印出来(不仅仅是在某些过程中按Enter键后删除)。

我的老师给了我一个汇编语言的任务。目前我在 Linux 上使用 NASM 并希望坚持下去。

问题是我需要“重载”用户输入中断,并且在其中我应该能够处理该输入(跳过每个辅音)。我已经搜索了网络并没有找到任何合适的答案。不是如何在缓冲和打印出来之前“捕获”用户输入,也不是在终端中不打印就读取单个字符的方法。

我想看一个替换(或修改)标准输入中断系统调用的示例。那将是理想的情况。

第二种选择是让您自己的“处理程序”类型一个接一个地获取每个字符而不回显它,而不是自己处理诸如 Backspace 和 Enter 之类的事情。就像 Windows C 中的 getch() 一样。 (或 0x16 BIOS 中断)。

【问题讨论】:

  • this code 翻译成汇编。
  • 确定您正确解释了作业吗?您可能应该读取输入,然后打印它的过滤副本。如果您从 tty 读取,您想要的需要其他 POSIX TTY 系统调用将终端设置为无回显甚至原始模式。例如./my_program < input_file 也可以。
  • 不,它应该像这样工作:用户:Presses R 终端:R 用户:Presses f 终端:R 用户:Presses z 终端:Rz 等等……
  • @Jester 我会努力的,谢谢
  • 请注意,没有可以挂钩或修改的“中断”或“处理程序”。 UNIX(和 Linux)不能以这种方式工作。相反,正如 Jester 所说,您需要重新配置终端子系统 termios 以不回显用户键入的文本。除了 Jester 链接的答案之外,我建议您仔细阅读 Linux 手册页 man 3 termiosman 4 tty_ioctl,其中解释了 termios 子系统的工作原理以及如何在没有 libc 的情况下对其进行配置。请记住,这是在汇编中执行的一项高级任务,您需要通过查看标题手动构建正确的结构。

标签: linux assembly input nasm tty


【解决方案1】:

这是我用来检索单个击键的示例。请记住,功能键、箭头和其他,如 HOME、PGDN 等返回超过一个字节,因此我最多读取 8 个字节,因此输入缓冲区中剩余的内容不会成为下一次写入的工件。这个 sn-p 被设计为对以下事物的响应系统;

继续 [是/否]

调用进程将读取 AL 中返回的字符。如果它是 0x1b (27 dec / ESC),那么我们知道它是扩展键之一。例如,F1 将在 EAX 中返回 0x504f1b。

USE64

      sys_read      equ      0
      sys_write     equ      1
      sys_ioctl     equ     16

      %define      ICANON      2
      %define        ECHO      8
      %define      TCGETS      0x5401
      %define      TCPUTS      0X5402

            section    .text
    ; =============================================================================
    ; Accept a single key press from operator and return the result that may be
    ; up to 5 bytes in length.

    ;    LEAVE: RAX = Byte[s] returned by SYS_READ
    ; -----------------------------------------------------------------------------

      %define   c_lflag     rdx + 12
      %define      keys     rbp +  8
      %define      MASK     ICANON | ECHO

      STK_SIZE  equ 56              ; Room for 36 byte termios structure

      QueryKey:

            xor     eax, eax
            push    rax             ; This is where result will be stored.

            push    rbp
            mov     rbp, rsp
            sub     rsp, STK_SIZE

            push    r11             ; Modified by SYSCALL
            push    rbx
            push    rdx
            push    rcx
            push    rdi
            push    rsi             ; With size of 56, stack is now QWORD aligned

            mov     edi, eax            ; Equivalent to setting EDI to STDIN
            mov     esi, TCGETS
            lea     rdx, [rbp-STK_SIZE] ; Points to TERMIOS buffer on stack
            mov      al, sys_ioctl
            syscall

            lea     rbx, [c_lflag]
            and     byte [rbx], ~(MASK)
            inc     esi                 ; RSI = TCPUTS
            push    rsi
            mov      al, sys_ioctl
            push    rax
            syscall

       ; Wait for keypress from operator.

            lea     rsi, [keys]         ; Set buffer for input
            push    rdx
            mov     edx, 8              ; Read QWORD bytes max
            mov      al, sys_read
            syscall

NOTE: The code you need could go here

            pop     rdx                 ; Points back to TERMIOS
            pop     rax
            pop     rsi                 ; TCPUTS again
            or      byte [rbx], MASK
            syscall

            pop     rsi
            pop     rdi
            pop     rcx
            pop     rdx
            pop     rbx
            pop     r11

            leave
            pop        rax              ; Return up to 8 characters
            ret

【讨论】:

  • xor eax,eax在顶部和mov al, imm8在后面混淆你的代码不是一个好主意;所需的系统调用已经足够复杂和模糊。此外,如果标准输入不是 TTY,第一个系统调用将返回 -ENOTTY,第二个系统调用将使用 RAX=FFFF...??并返回 -ENOSYS。在这种情况下,至少使用mov eax, sys_read 仍然是read(0, buf, 8)
  • 将寄存器放入%define 也会使代码更难阅读,IMO。起初我假设lea rbx, [c_lflag] 是静态存储(而你忘记了default rel)。 lea rbx, [rdx + c_lflag] 将更具可读性 IMO,只需一个用于结构布局内容的 EQU 常量。
  • @PeterCordes 混淆是一种范式,虽然我认为这并非完全没有根据,但仍在进行中的工作以评估何时真正有必要。根据您的批评,我将节省一个字节而不是push 0,然后是mov eax,sys_ioctl。我倾向于更详细的 cmets 以使意图更清晰。
  • 虽然混淆不是动机,但结果却是让它更难阅读。 IMO 只需使用mov 并将优化留给读者。如果您正在优化,请不要使用rbp。将输出缓冲区放在保存的 RBP 之上并没有什么特别的好处。您可以在 syscall 之前使用 push rax / mov rsi, rsp。如果读取了 1 个或多个字节,它将覆盖缓冲区的非零低字节。
  • 谢谢@Shift_Left!它就像一个魅力!但是我无法确定检查输入的符号是退格(我猜是 0x08 是代码)还是 Enter。你能帮我弄清楚吗?其他角色对我来说效果很好。
猜你喜欢
  • 2016-07-28
  • 2013-08-03
  • 1970-01-01
  • 2019-10-06
  • 2018-03-19
  • 1970-01-01
  • 2011-05-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多