简体   繁体   English

为什么即使在非规范模式下也要按Enter键才能读取和输出输入?

[英]Why I still need to press 'Enter" in order to let input be read and output even in non-canonical mode?

I was testing this code from the GNU libc manual: 我正在从GNU libc手册中测试以下代码:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>

/* Use this variable to remember original terminal attributes. */

struct termios saved_attributes;

void
reset_input_mode (void)
{
  tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes);
}

void
set_input_mode (void)
{
  struct termios tattr;
  char *name;

  /* Make sure stdin is a terminal. */
  if (!isatty (STDIN_FILENO))
    {
      fprintf (stderr, "Not a terminal.\n");
      exit (EXIT_FAILURE);
    }

  /* Save the terminal attributes so we can restore them later. */
  tcgetattr (STDIN_FILENO, &saved_attributes);
  atexit (reset_input_mode);

  /* Set the funny terminal modes. */
  tcgetattr (STDIN_FILENO, &tattr);
  tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
  tattr.c_cc[VMIN] = 1;
  tattr.c_cc[VTIME] = 0;
  tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr);
}

int
main (void)
{
  char c;

  set_input_mode ();

  while (1)
    {
      read (STDIN_FILENO, &c, 1);
      if (c == '\004')          /* C-d */
        break;
      else
        putchar (c);
    }

  return EXIT_SUCCESS;
}

Even though the terminal was set to be in non-canonical mode, I still need to press enter for input to be received. 即使终端设置为非规范模式,我仍然需要按Enter才能接收输入。

However, if I change: putchar(c) to write(STDOUT_FILENO, &c, sizeof(char)) , it works properly as I thought. 但是,如果我更改: putchar(c) write(STDOUT_FILENO, &c, sizeof(char)) ,它可以按照我的想法正常工作。

You are being trumped by user buffering! 您正在被用户缓冲所压倒! putchar(3) is part of libc I/O API, while write(2) is a system call -- well, not quite a system call, but for simplicity's sake, let's consider it is for now. putchar(3)是libc I / O API的一部分,而write(2)是系统调用-嗯,这不完全是系统调用,但是为了简单起见,现在考虑一下。

There are three types of buffering in libc: unbuffered, block buffered, and line buffered. libc中有三种缓冲类型:未缓冲,块缓冲和行缓冲。 If a stream is unbuffered, data goes to the underlying file (or terminal) as soon as it is written; 如果流没有缓冲,则数据在写入后立即进入底层文件(或终端)。 if it's block buffered, data is saved in the memory block until it fills up and then it's written all at once; 如果是块缓冲的,则将数据保存在内存块中,直到数据填满,然后立即将其全部写入。 however, if it's line buffered, data is transmitted to the file (or terminal) when a newline character is found. 但是,如果是行缓冲的,则当找到换行符时,数据将传输到文件(或终端)。

If a stream is connected to a terminal, as normally is the case of standard output, it's line buffered. 如果将流连接到终端(通常是标准输出的情况),则会对其进行行缓冲。 So, this is your case: when you press enter, the newline character \\n causes the (line) buffer to be written to standard output. 因此,这是您的情况:当您按Enter键时,换行符\\n导致(行)缓冲区被写入标准输出。 However, when you call write(2) , libc user buffering is bypassed and data is written to the corresponding file descriptor (STDOUT_FILENO). 但是,当您调用write(2) ,将绕过libc 用户缓冲 ,并将数据写入相应的文件描述符 (STDOUT_FILENO)。

So, as I said earlier, write(2) is a system call; 因此,正如我之前所说, write(2)是一个系统调用; but in truth, when you call write , you are calling a library wrapper to the system call, which handles the strict protocol the system call follows (eg, where it expects arguments, etc). 但实际上,当您调用write ,您是在对系统调用进行库包装,该系统处理了系统调用遵循的严格协议(例如,期望参数的位置等)。

By the way, everything I said here can be found in the man pages for putchar(3) , write(2) , setbuf(3) . 顺便说一句,我在这里所说的所有内容都可以在putchar(3)write(2)setbuf(3)的手册页中找到。 The numbers in parentheses refer to the section in the manual: 2 is for system calls, 3 is for library functions ( man man should give you a list of sections and their topic). 括号中的数字指的是手册中的部分2用于系统调用, 3用于库函数( man man应该给您列出节及其主题)。

Hope it helps. 希望能帮助到你。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM