2010-12-01 132 views
7

我的测试应用程序是非缓冲标准输入读取

#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终端的默认模式I/O是面向行的,其意味着您将始终需要输入的\n字符来检索输入。您需要使用终端I/O设施,以便您在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驱动程序立即为您提供字节。有各种库使这比使用原始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; 
}