非缓冲stdin阅读

Xim*_*mik 7 c unix linux posix

我的测试应用程序是

#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;
}
Run Code Online (Sandbox Code Playgroud)

我希望在每次输入char后看到char-code.但实际上*s仅在控制台中的"\n"之后打印.所以似乎stdin(带有desc 0的文件)被缓冲了.但是read函数是无缓冲的,不是吗?哪里错了.

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;
} 
Run Code Online (Sandbox Code Playgroud)

Cha*_*via 12

遗憾的是,使用标准ANSI C无法实现您正在寻找的行为,并且UNIX终端I/O的默认模式是面向行的,这意味着您始终需要输入的\n字符来检索输入.您需要使用终端I/O工具,让您以非规范模式进行编程,以便每次按键触发一个事件.在Linux/UNIX上,您可以查看<termios.h>标头或ncurses库.

  • @Ximik,是的,他们没有使用标准的ANSI C.大多数使用外部库,如ncurses或termcap. (4认同)

Sam*_*man 5

在我看来,您的解决方案有点复杂。还是不明白为什么需要管道和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;
}
Run Code Online (Sandbox Code Playgroud)