без буферизации чтения стандартного ввода - PullRequest
7 голосов
/ 01 декабря 2010

Мое тестовое приложение -

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

Ответы [ 3 ]

12 голосов
/ 01 декабря 2010

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

4 голосов
/ 30 декабря 2014

Мне кажется, что ваше решение немного сложнее.До сих пор не понимаю, зачем вам труба и 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;
}
2 голосов
/ 01 декабря 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...