2016-10-02 5 views
0

Я проверял this код из LibC руководства GNU:Почему мне все еще нужно нажать «Enter», чтобы позволить ввод читаться и выход даже в неканонической режиме

#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; 
} 

Даже если терминал было установлено, что в нестандартном режиме, мне еще нужно нажать ввести для ввода для приема

Однако, если я изменю:. putchar(c) к write(STDOUT_FILENO, &c, sizeof(char)), он работает правильно, как я думал

ответ

2

Вы в настоящее время. с помощью буферизации пользователя! putchar(3) является частью API ввода-вывода libc, а write(2) - системный вызов - ну, не совсем системный вызов ,, но, для простоты, давайте рассмотрим его пока.

Существует три типа буферизации в libc: небуферизованные, буферизированные по блокам и буферизация строк. Если поток небуферизован, данные поступают в основной файл (или терминал), как только он записывается; если он заблокирован блоком, данные сохраняются в блоке памяти до тех пор, пока он не заполнится, а затем все будет записано сразу; однако, если он привязан к линии, данные передаются в файл (или терминал), когда найден символ новой строки.

Если поток подключен к терминалу, как обычно, это стандартный вывод, он буферизируется по строке. Итак, это ваш случай: когда вы нажимаете клавишу ввода, символ новой строки \n заставляет (линейный) буфер записываться на стандартный вывод. Однако, когда вы вызываете write(2), libc пользовательский буферизатор обходит и данные записываются в соответствующий дескриптор файла (STDOUT_FILENO).

Так, как я сказал ранее, write(2) - системный вызов; но на самом деле, когда вы вызываете write, вы вызываете библиотечный обертку на системный вызов, который обрабатывает строгий протокол, за которым следует системный вызов (например, где он ожидает аргументы и т. д.).

Кстати, все, что я сказал здесь, можно найти на страницах руководства для putchar(3), write(2), setbuf(3). Цифры в скобках относятся к разделу в руководстве: 2 предназначен для системных вызовов, 3 предназначен для библиотечных функций (man man должен предоставить вам список разделов и их тематику).

Надеюсь, это поможет.

+1

Вы можете упомянуть 'setvbuf (stdout, 0, _IONBF, 0)' как способ получить 'putchar()' для немедленного эха. Или, может быть, я сделал это, и этого достаточно. (Возможно, вам захочется подумать о том, чтобы что-то сделать, чтобы убедить вас, что персонажи, которые вы видите на выходе, не то, что вы обычно видите эхом от терминала, например, используя 'putchar ('['), putchar (c), putchar (']'); 'вместо' putchar (c); '.) –