Как вы делаете неблокирующий консольный ввод / вывод в Linux в C? - PullRequest
30 голосов
/ 04 апреля 2009

Как вы делаете неблокирующий консольный ввод-вывод в Linux / OS X в C?

Ответы [ 8 ]

19 голосов
/ 15 августа 2009

Как и Пит Киркхам , я нашел cc.byexamples.com , и это сработало для меня. Здесь вы найдете хорошее объяснение проблемы, а также версию ncurses.

Мой код должен был взять начальную команду из стандартного ввода или файла, а затем наблюдать за командой отмены, пока исходная команда была обработана. Мой код на C ++, но вы должны быть в состоянии использовать scanf () и остальные, где я использую функцию ввода C ++ getline ().

Мясо - это функция, которая проверяет, есть ли доступный ввод:

#include <unistd.h>
#include <stdio.h>
#include <sys/select.h>

// cc.byexamples.com calls this int kbhit(), to mirror the Windows console
//  function of the same name.  Otherwise, the code is the same.
bool inputAvailable()  
{
  struct timeval tv;
  fd_set fds;
  tv.tv_sec = 0;
  tv.tv_usec = 0;
  FD_ZERO(&fds);
  FD_SET(STDIN_FILENO, &fds);
  select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
  return (FD_ISSET(0, &fds));
}

Это должно быть вызвано перед любой функцией ввода stdin. Когда я использовал std :: cin перед использованием этой функции, она никогда больше не возвращала true. Например, main () имеет цикл, который выглядит следующим образом:

int main(int argc, char* argv[])
{ 
   std::string initialCommand;
   if (argc > 1) {
      // Code to get the initial command from a file
   } else {
     while (!inputAvailable()) {
       std::cout << "Waiting for input (Ctrl-C to cancel)..." << std::endl;
       sleep(1);
     }
     std::getline(std::cin, initialCommand);
   }

   // Start a thread class instance 'jobThread' to run the command
   // Start a thread class instance 'inputThread' to look for further commands
   return 0;
}

Во входном потоке новые команды были добавлены в очередь, которая периодически обрабатывалась заданием jobThread. InputThread выглядела примерно так:

THREAD_RETURN inputThread()
{
  while( !cancelled() ) {
    if (inputAvailable()) {
      std::string nextCommand;
      getline(std::cin, nextCommand);
      commandQueue.lock();
      commandQueue.add(nextCommand);
      commandQueue.unlock();
    } else {
        sleep(1);
    }
  }
  return 0;
}

Эта функция, возможно, могла быть в main (), но я работаю с существующей кодовой базой, а не против нее.

Для моей системы не было доступного ввода до отправки новой строки, что было именно тем, что я хотел. Если вы хотите прочитать каждый символ при вводе текста, вам нужно отключить «канонический режим» в stdin. cc.byexamples.com содержит некоторые предложения, которые я не пробовал, но остальные сработали, поэтому он должен работать.

11 голосов
/ 30 мая 2015

Я хочу добавить пример:

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>

int main(int argc, char const *argv[])

{
    char buf[20];
    fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
    sleep(4);
    int numRead = read(0,buf,4);
    if(numRead > 0){
        printf("You said: %s", buf);
    }
}

Когда вы запускаете эту программу, у вас есть 4 секунды, чтобы обеспечить ввод для стандартного входа. Если вход не найден, он не заблокируется и просто вернется.

2 образца исполнения:

Korays-MacBook-Pro:~ koraytugay$ ./a.out
fda 
You said: fda
Korays-MacBook-Pro:~ koraytugay$ ./a.out
Korays-MacBook-Pro:~ koraytugay$ 
11 голосов
/ 04 апреля 2009

На самом деле нет. TTY (консоль) - довольно ограниченное устройство, и вы практически не делаете неблокирующий ввод / вывод. То, что вы делаете, когда видите что-то похожее на неблокирующий ввод / вывод, скажем, в приложении curses / ncurses, называется raw I / O . В необработанном вводе-выводе нет никакой интерпретации символов, никакой обработки стирания и т. Д. Вместо этого вам нужно написать собственный код, который проверяет данные при выполнении других действий.

В современных программах на Си вы можете упростить это другим способом, поместив консольный ввод / вывод в поток или упрощенный процесс. Затем ввод / вывод может продолжаться обычным способом блокировки, но данные могут быть вставлены в очередь для обработки в другом потоке.

Обновление

Вот учебник curses , который охватывает его больше.

6 голосов
/ 04 апреля 2009

Я отметил « Неблокируемый пользовательский ввод в цикле без ncurses » ранее в этом месяце, когда я подумал, что мне может понадобиться неблокируемый небуферизованный консольный ввод, но я этого не сделал, поэтому могу ручаюсь за то, работает это или нет. Для моего использования мне было все равно, что он не получал ввод, пока пользователь не нажал Enter, поэтому просто использовал aio для чтения стандартного ввода.

4 голосов
/ 04 апреля 2009
3 голосов
/ 04 апреля 2009

использование ncurses

2 голосов
/ 15 августа 2009

Другой альтернативой использованию ncurses или потоков является использование GNU Readline , особенно той его части, которая позволяет регистрировать функции обратного вызова . Шаблон тогда:

  1. Используйте select () для STDIN (среди любых других дескрипторов)
  2. Когда select () сообщает, что STDIN готов к чтению, вызовите readl's rl_callback_read_char ()
  3. Если пользователь ввел полную строку, rl_callback_read_char будет вызывать ваш обратный вызов. В противном случае он немедленно вернется, и ваш другой код может продолжиться.
0 голосов
/ 04 апреля 2009

Не совсем уверен, что вы подразумеваете под «консольным вводом-выводом» - вы читаете из STDIN, или это консольное приложение, которое читает из какого-то другого источника?

Если вы читаете из STDIN, вам нужно пропустить fread () и использовать read () и write () с poll () или select (), чтобы запретить вызовы. Возможно, вы сможете отключить буферизацию ввода, что должно заставить fread возвращать EOF с помощью setbuf (), но я никогда не пробовал.

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