2016-10-02 3 views
0

je testais this le code du manuel GNU libc:Pourquoi je dois encore appuyer sur « Enter » afin de laisser l'entrée être lu et sortie même en mode non canonique

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

Même si le terminal a été paramétré en mode non-canonique, je dois encore appuyer sur entrée pour l'entrée à recevoir Cependant

, si je change. putchar(c)-write(STDOUT_FILENO, &c, sizeof(char)), cela fonctionne bien que je pensais

Répondre

2

Vous êtes. trompé par l'utilisateur tampon! putchar(3) fait partie de libc I/O API, tandis que write(2) est un appel système - bien, pas tout à fait un appel système , mais pour la simplicité, considérons que c'est pour l'instant.

Il existe trois types de tampons dans libc: unbuffered, block buffer, et buffered line. Si un flux n'est pas tamponné, les données sont transmises au fichier sous-jacent (ou au terminal) dès qu'elles sont écrites; si le bloc est tamponné, les données sont sauvegardées dans le bloc de mémoire jusqu'à ce qu'elles se remplissent, puis elles sont écrites toutes en même temps; cependant, si la ligne est mise en mémoire tampon, les données sont transmises au fichier (ou au terminal) lorsqu'un caractère de retour à la ligne est trouvé.

Si un flux est connecté à un terminal, comme c'est normalement le cas pour la sortie standard, il s'agit d'une ligne tampon. Donc, c'est votre cas: lorsque vous appuyez sur Entrée, le caractère de nouvelle ligne \n provoque l'écriture du tampon (ligne) sur la sortie standard. Toutefois, lorsque vous appelez write(2), libc la mise en mémoire tampon utilisateur est ignorée et les données sont écrites dans le descripteur de fichier correspondant (STDOUT_FILENO). Donc, comme je l'ai dit plus tôt, write(2) est un appel système; mais en vérité, lorsque vous appelez write, vous appelez un wrapper de bibliothèque à l'appel système, qui gère le protocole strict suivi par l'appel système (par exemple, où il attend des arguments, etc). Par ailleurs, tout ce que j'ai dit ici peut être trouvé dans les pages man pour putchar(3), write(2), setbuf(3). Les numéros entre parenthèses se réfèrent à la section dans le manuel: 2 est pour les appels système, 3 est pour les fonctions de la bibliothèque (man man devrait vous donner une liste des sections et de leur sujet).

Espérons que ça aide.

+1

Vous pouvez mentionner 'setvbuf (stdout, 0, _IONBF, 0)' comme moyen d'obtenir immédiatement l'écho de 'putchar()'. Ou peut-être que je l'ai fait et c'est suffisant. (Vous voudrez probablement penser à faire quelque chose pour vous convaincre que les caractères que vous voyez sur la sortie ne sont pas ce que vous voyez habituellement en écho avec le terminal, comme en utilisant 'putchar ('['), putchar (c), putchar (']'); 'à la place de' putchar (c); '.) –