How do you do non-blocking console I/O on Linux in C? How do you do non-blocking console I/O on Linux in C? c c

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.