How do you do non-blocking console I/O on Linux in C?
I want to add an example:
#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); }}
When you run this program you have 4 seconds to provide input to standard in. If no input found, it will not block and will simply return.
2 sample executions:
Korays-MacBook-Pro:~ koraytugay$ ./a.outfda You said: fdaKorays-MacBook-Pro:~ koraytugay$ ./a.outKorays-MacBook-Pro:~ koraytugay$
Like Pete Kirkham, I found cc.byexamples.com, and it worked for me. Go there for a good explanation of the problem, as well as the ncurses version.
My code needed to take an initial command from standard input or a file, then watch for a cancel command while the initial command was processed. My code is C++, but you should be able to use scanf()
and the rest where I use the C++ input function getline()
.
The meat is a function that checks if there is any input available:
#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));}
This has to be called before any stdin input function When I used std::cin
before using this function, it never returned true again. For example, main()
has a loop that looks like this:
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;}
In the input thread, new commands were added to a queue, which was periodically processed by the jobThread
. The inputThread
looked a little like this:
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;}
This function probably could have been in main()
, but I'm working with an existing codebase, not against it.
For my system, there was no input available until a newline was sent, which was just what I wanted. If you want to read every character when typed, you need to turn off "canonical mode" on stdin. cc.byexamples.com has some suggestions which I haven't tried, but the rest worked, so it should work.
You don't, really. The TTY (console) is a pretty limited device, and you pretty much don't do non-blocking I/O. What you do when you see something that looks like non-blocking I/O, say in a curses/ncurses application, is called raw I/O. In raw I/O, there's no interpretation of the characters, no erase processing etc. Instead, you need to write your own code that checks for data while doing other things.
In modern C programs, you can simplify this another way, by putting the console I/O into a thread or lightweight process. Then the I/O can go on in the usual blocking fashion, but the data can be inserted into a queue to be processed on another thread.
Update
Here's a curses tutorial that covers it more.