Как вы делаете неблокирующую консоль ввода-вывода в Linux/OS X в C?
Как вы делаете неблокирующий консольный ввод-вывод в Linux на C?
Ответ 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.
Ответ 5
Вот связанный с этим вопрос, используя С++ - Кросс-платформенный (linux/Win32) неблокирующий С++ IO на stdin/stdout/stderr
Ответ 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(), но я никогда не пробовал его.