你如何做在Linux中的非阻塞控制台I / O在C?

你如何做在Linux / OS X上的非阻塞控制台IO?

你没有,真的。 TTY(控制台)是一个非常有限的设备,你几乎不做非阻塞的I / O。 当你看到一些看起来像非阻塞I / O的东西,比如说在curses / ncurses应用程序中,就是所谓的原始I / O。 在原始I / O中,没有对字符的解释,也没有擦除处理等。相反,您需要编写自己的代码,在执行其他事情时检查数据。

在现代C程序中,您可以通过将控制台I / O放入线程或轻量级进程来简化这一操作。 然后I / O可以以通常的阻塞方式继续,但可以将数据插入队列中以在另一个线程上处理。

更新

这是一个诅咒教程 ,涵盖了更多。

像Pete Kirkham一样,我找到了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有一些建议,我没有尝试,但其余的工作,所以它应该工作。

我想添加一个例子:

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

我在本月早些时候收到了 “ 无阻塞用户输入循环,没有ncurses ”,当时我想我可能需要非阻塞,非缓冲的控制台输入,但是我没有,所以不能保证它是否有效。 对于我的使用,我不在乎它没有得到输入,直到用户点击进入,所以只是用aio阅读标准输入。

这里有一个相关的问题使用C ++ – 跨平台(Linux / Win32)非阻塞C + + IO在标准输入/标准输出/标准错误

使用ncurses

使用ncurses或线程的另一种方法是使用GNU Readline ,特别是它的一部分,它允许你注册回调函数 。 模式是:

  1. 在STDIN上使用select()(在任何其他描述符中)
  2. 当select()告诉你STDIN准备好读取时,调用readline的rl_callback_read_char()
  3. 如果用户输入了一个完整的行,rl_callback_read_char将会调用你的回调函数。 否则,它会立即返回,您的其他代码可以继续。

不完全确定你的意思是“控制台IO” – 你是从STDIN读取的,还是这是一个控制台应用程序从其他来源读取?

如果你正在读STDIN,你需要跳过fread(),并使用read()和write(),使用poll()或select()来阻止调用。 您可能能够禁用输入缓冲,这将导致fread返回一个EOF,与setbuf(),但我从来没有尝试过。