如何使用C?在Linux / OS X上进行非阻塞控制台IO?
答案 0 :(得分:19)
与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有一些我没有尝试过的建议,但其余的都有效,所以应该有效。
答案 1 :(得分:11)
你没有,真的。 TTY(控制台)是一个非常有限的设备,你几乎不做非阻塞I / O.当您看到类似非阻塞I / O的内容(例如在curses / ncurses应用程序中)时,您所执行的操作称为原始I / O 。在原始I / O中,没有对字符的解释,没有擦除处理等。相反,您需要编写自己的代码来检查数据,同时做其他事情。
在现代C程序中,您可以通过将控制台I / O放入线程或轻量级进程来简化这种方式。然后I / O可以以通常的阻塞方式继续,但是数据可以插入到队列中以便在另一个线程上处理。
这是一个涵盖更多内容的curses tutorial。
答案 2 :(得分:11)
我想添加一个例子:
#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$
答案 3 :(得分:6)
本月早些时候,当我认为我可能需要非阻塞,非缓冲控制台输入时,我将“Non-blocking user input in loop without ncurses”加入书签,但我没有,因此无法保证它是否有效。对于我的使用,我并不关心它在用户点击输入之前没有输入,所以只是使用aio来读取标准输入。
答案 4 :(得分:4)
答案 5 :(得分:3)
使用ncurses
答案 6 :(得分:2)
使用ncurses或线程的另一种方法是使用GNU Readline,特别是允许你register callback functions的部分。那么模式是:
答案 7 :(得分:0)
不完全确定“控制台IO”的含义 - 您是从STDIN读取的,还是从其他来源读取的控制台应用程序?
如果您正在读取STDIN,则需要跳过fread()并使用read()和write(),使用poll()或select()来阻止调用。您可以使用setbuf()禁用输入缓冲,这应该会导致fread返回EOF,但我从未尝试过。