2010-12-01 4 views
7

Моего тестового приложения являетсябез буферизации STDIN чтения

#include <sys/types.h> 
#include <sys/wait.h> 
#include <signal.h> 
#include <unistd.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <fcntl.h> 

int main(int argc, char *argv[], char *envp[]) { 
    int fd[2]; 

    if(pipe(fd) < 0) { 
    printf("Can\'t create pipe\n"); 
    exit(-1); 
    } 

    pid_t fpid = fork(); 
    if (fpid == 0) { 
    close(0); 
    close(fd[1]); 
    char *s = (char *) malloc(sizeof(char)); 
    while(1) if (read(fd[0], s, 1)) printf("%i\n", *s); 
    } 
    close(fd[0]); 
    char *c = (char *) malloc(sizeof(char)); 
    while (1) { 
    if (read(0, c, 1) > 0) write(fd[1], c, 1); 
    } 
    return 0; 
} 

Я хочу видеть обугленный-код после каждого введенного полукокса. Но на самом деле * s печатается только после «\ n» в консоли. Кажется, что stdin (файл с desc 0) буферизуется. Но функция чтения не имеет буферности, не так ли? Где я ошибаюсь.

UPD: Я использую linux.

Так решение

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

int main(int argc, char *argv[], char *envp[]) { 
    int fd[2]; 

    if(pipe(fd) < 0) { 
    printf("Can\'t create pipe\n"); 
    exit(-1); 
    } 

    struct termios term, term_orig; 

    if(tcgetattr(0, &term_orig)) { 
    printf("tcgetattr failed\n"); 
    exit(-1); 
    } 

    term = term_orig; 

    term.c_lflag &= ~ICANON; 
    term.c_lflag |= ECHO; 
    term.c_cc[VMIN] = 0; 
    term.c_cc[VTIME] = 0; 

    if (tcsetattr(0, TCSANOW, &term)) { 
    printf("tcsetattr failed\n"); 
    exit(-1); 
    } 

    pid_t fpid = fork(); 
    if (fpid == 0) { 
    close(0); 
    close(fd[1]); 
    char *s = (char *) malloc(sizeof(char)); 
    while(1) if (read(fd[0], s, 1)) printf("%i\n", *s); 
    } 
    close(fd[0]); 
    char *c = (char *) malloc(sizeof(char)); 
    while (1) { 
    if (read(0, c, 1) > 0) write(fd[1], c, 1); 
    } 
    return 0; 
} 
+1

Обратите внимание, что это не имеет никакого отношения к буферизации. – 2010-12-01 19:05:31

ответ

12

К сожалению, поведение вы ищете не возможно со стандартом ANSI C, и режим по умолчанию для UNIX терминала ввода/вывода строки-ориентированной, которая означает, что вам всегда понадобится введенный символ \n для извлечения ввода. Вам понадобятся терминальные устройства ввода-вывода, которые позволяют программировать в режиме non-canonical, так что каждое нажатие клавиши вызывает событие. В Linux/UNIX вы можете посмотреть заголовок <termios.h> или библиотеку ncurses.

+0

Но есть много приложений, которые делают это: man, vim и т. Д. – Ximik 2010-12-01 18:58:17

+4

@Ximik, да, и они не используют стандартный ANSI C. Большинство используют внешние библиотеки, такие как ncurses или termcap. – 2010-12-01 18:59:08

2

Unix буферизирует ваши символы tty внутри ядра частично, так что программам не нужно индивидуально обрабатывать редактирование строк, если они этого не хотят.

Вы можете дать указание драйверу tty немедленно предоставить вам байты. Существуют различные библиотеки, которые делают это немного легче, чем использование raw ioctl. Вы можете начать с termios(3).

3

Мне кажется, что ваше решение немного сложнее. Все еще не понимаю, зачем вам труба и 2 процесса.

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

int main(int argc, char *argv[], char *envp[]) { 
    struct termios term, term_orig; 

    if(tcgetattr(0, &term_orig)) { 
    printf("tcgetattr failed\n"); 
    exit(-1); 
    } 

    term = term_orig; 

    term.c_lflag &= ~ICANON; 
    term.c_lflag |= ECHO; 
    term.c_cc[VMIN] = 0; 
    term.c_cc[VTIME] = 0; 

    if (tcsetattr(0, TCSANOW, &term)) { 
    printf("tcsetattr failed\n"); 
    exit(-1); 
    } 

    char ch; 
    while (1) { 
    if (read(0, &ch, 1) > 0) 
     printf(" %d\n", ch); 
    } 
    return 0; 
}