Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Linux 如何在汇编中逐个字符地从标准输入读取输入_Linux_Assembly_X86 64_Nasm_System Calls - Fatal编程技术网

Linux 如何在汇编中逐个字符地从标准输入读取输入

Linux 如何在汇编中逐个字符地从标准输入读取输入,linux,assembly,x86-64,nasm,system-calls,Linux,Assembly,X86 64,Nasm,System Calls,我希望下面的程序能够从stdin中读取一些字符(最多9个),并将它们放在内存中的指定位置 实际发生的情况:当我按Enter键时,如果少于9个字符,它只会转到下一行;这将一直发生,直到我输入9个字符。如果输入的字符数超过9,多余的字符将被解释为shell命令。为什么我按Enter键时它不终止 在Ubuntu上使用nasm2.14.02 global _start section .bss buf resb 10 section .text ; Read a word f

我希望下面的程序能够从
stdin
中读取一些字符(最多9个),并将它们放在内存中的指定位置

实际发生的情况:当我按Enter键时,如果少于9个字符,它只会转到下一行;这将一直发生,直到我输入9个字符。如果输入的字符数超过9,多余的字符将被解释为shell命令。为什么我按Enter键时它不终止

在Ubuntu上使用
nasm
2.14.02

  global _start
  section .bss
    buf resb 10
  section .text
    ; Read a word from stdin, terminate it with a 0 and place it at the given address.
    ; - $1, rdi: *buf - where to place read bytes
    ; - $2, rsi: max_count, including the NULL terminator
    ; Returns in rax:
    ; - *buf - address of the first byte where the NULL-terminated string was placed
    ; - 0, if input too big
    read_word: ; (rdi: *buf, rsi: max_count) -> *buf, or 0 if input too big
      mov r8, 0      ; current count
      mov r9, rsi    ; max count
      dec r9         ; one char will be occupied by the terminating 0

      ; read a char into the top of the stack, then pop it into rax
      .read_char:
        push rdi       ; save; will be clobbered by syscall
        mov rax, 0     ; syscall id = 0 (read)
        mov rdi, 0     ; syscall $1, fd = 0 (stdin)
        push 0         ; top of the stack will be used to place read byte
        mov rsi, rsp   ; syscall $2, *buf = rsp (addr where to put read byte)
        mov rdx, 1     ; syscall $3, count (how many bytes to read)
        syscall
        pop rax
        pop rdi

      ; if read character is Enter (aka carriage-return, CR) - null-terminate the string and exit
      cmp rax, 0x0d ; Enter
      je .exit_ok

      ; not enter ⇒ place it in the buffer, and read another one
      mov byte [rdi+r8], al ; copy character into output buffer
      inc r8                ; inc number of collected characters
      cmp r8, r9            ; make sure number doesn't exceed maximum
      je .exit_ok           ; if we have the required number of chars, exit
      jb .read_char         ; if it's not greater, read another char

      .exit_ok: ; add a null to the end of the string and return address of buffer (same as input)
        add r8, 1
        mov byte [rdi+r8], 0
        mov rax, rdi
        ret

      .exit_err: ; return 0 (error)
        mov rax, 0
        ret

  _start:
    mov rdi, buf     ; $1 - *buf
    mov rsi, 10      ; $2 - uint count
    call read_word

    mov rax, 60  ; exit syscall
    mov rdi, 0   ; exit code
    syscall


首先,当用户点击Enter时,您将看到LF(
\n
0xa
),而不是CR(
\r
0xd
)。这也许可以解释为什么你的程序没有在你认为应该退出的时候退出

至于为什么额外的字符会进入shell,这是关于操作系统如何进行终端输入。它将来自终端的击键累积到内核缓冲区中,直到按下Enter键,然后使整个缓冲区可供
read()
读取。这使得backspace之类的东西可以透明地工作,而不需要应用程序显式地编写代码,但这确实意味着,正如您所注意到的,您不能一次读取一个击键

如果您的程序在缓冲区仍包含字符时退出,那么下一个试图从设备读取字符的程序将读取这些字符,在您的情况下,该程序将是shell。大多数读取stdin的程序通过继续读取和处理数据来避免这种情况,直到看到文件结尾(
read()
返回0),当用户按下Ctrl-D时,终端会出现这种情况


如果您确实需要逐个字符地处理输入,则需要将终端设置为,但在这种情况下,许多事情会有所不同。

首先,当用户点击Enter键时,您将看到LF(
\n
0xa
),而不是CR(
\r
0xd
)。这也许可以解释为什么你的程序没有在你认为应该退出的时候退出

至于为什么额外的字符会进入shell,这是关于操作系统如何进行终端输入。它将来自终端的击键累积到内核缓冲区中,直到按下Enter键,然后使整个缓冲区可供
read()
读取。这使得backspace之类的东西可以透明地工作,而不需要应用程序显式地编写代码,但这确实意味着,正如您所注意到的,您不能一次读取一个击键

如果您的程序在缓冲区仍包含字符时退出,那么下一个试图从设备读取字符的程序将读取这些字符,在您的情况下,该程序将是shell。大多数读取stdin的程序通过继续读取和处理数据来避免这种情况,直到看到文件结尾(
read()
返回0),当用户按下Ctrl-D时,终端会出现这种情况


如果您确实需要逐个字符地处理输入,则需要将终端设置为,但在这种情况下,许多事情会有所不同。

系统调用可能会破坏rax、rcx、rdx、rsi、rdi、r8、r9、r10和r11中的任何一个或全部。您正在使用其中一些来保存数据,这样数据可能会丢失。@ChrisDodd干杯;在这个特殊的程序中,我认为只有
r8
r9
是正确的problematic@ChrisDodd:表示只有
rax、rcx、r11
被击倒。您可能正在考虑函数调用约定?@chrisdd:系统调用约定与函数调用约定不同。在整个ISA中,可以假设arg传递的regs没有被Linux系统调用修改,而不是返回值。(对于x86-64,RCX和R11也会被
syscall
指令本身阻塞。)
syscall
可能会阻塞rax、RCX、rdx、rsi、rdi、r8、r9、r10和R11中的任何一个或所有。您正在使用其中一些来保存数据,这样数据可能会丢失。@ChrisDodd干杯;在这个特殊的程序中,我认为只有
r8
r9
是正确的problematic@ChrisDodd:表示只有
rax、rcx、r11
被击倒。您可能正在考虑函数调用约定?@chrisdd:系统调用约定与函数调用约定不同。在整个ISA中,可以假设arg传递的regs没有被Linux系统调用修改,而不是返回值。(对于x86-64,RCX和R11也会被
syscall
指令本身击垮。)是的,将
LF
替换为
CR
技巧-程序终止时对外壳没有任何副作用吗是的,将
LF
替换为
CR
技巧-程序终止时对外壳没有任何副作用吗