Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

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

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

Update

Здесь curses tutorial, который охватывает его больше.

Ответ 2

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

Мой код должен был взять начальную команду из стандартного ввода или файла, а затем наблюдать за командой отмены во время обработки начальной команды. Мой код - С++, но вы должны иметь возможность использовать scanf(), а остальное, где я использую функцию ввода С++ 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 имеет некоторые предложения, которые я не пробовал, но все остальное работало, поэтому оно должно работать.

Ответ 3

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

#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$ 

Ответ 4

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

Ответ 6

использовать ncurses

Ответ 7

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

  • Используйте select() для STDIN (среди других дескрипторов)
  • Когда select() сообщает вам, что STDIN готов к чтению, вызовите readline rl_callback_read_char()
  • Если пользователь ввел полную строку, rl_callback_read_char вызовет ваш обратный вызов. В противном случае он будет немедленно возвращен, а ваш другой код может продолжить.

Ответ 8

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

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