select() on stdin when there is already data in stdin's buffer
The select function blocks the calling process until there is activity on any of the specified sets of file descriptors [...] A file descriptor is considered ready for reading if a read call will not block. (See: https://www.gnu.org/software/libc/manual/html_node/Waiting-for-I_002fO.html)
So I expected, that select in the following program would immediately return in the 2nd, ... iteration, if you enter a string > 4 characters in the first iteration. However it does not. Just after pressing any other key after the first output it continues to handle all the remaining input. Why?
Sample output:
./selectTest
12345678900
Keyboard input received: 1234
A
Keyboard input received: 5678
Keyboard input received: 900
Keyboard input received: A
Code
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
int main(void)
{
fd_set rfds;
char buf[100];
while(1)
{
FD_ZERO(&rfds);
FD_SET(fileno(stdin), &rfds);
if(-1 == select(FD_SETSIZE, &rfds, NULL, NULL, NULL))
{
perror("select() failedn");
}
if(FD_ISSET(fileno(stdin), &rfds))
{
printf("Keyboard input received: ");
fgets(buf, 5, stdin);
printf("%sn", buf);
}
}
return 0;
}
(I know, that I should not use select() anymore, but I am learning for an exam and we have to ...)
You are reading from a tty(4) (in the usual case when stdin is your terminal). These are tricky things, read the tty demystified.
Be aware that your terminal and its tty has some line discipline. So, some data is buffered in the kernel (and also in the standard library).
You might want to put your tty in raw mode. See termios(3) & stty(1)
But don't lose your time, instead use some library like ncurses or readline
To play with select
, you might use some fifo(7), perhaps with mkfifo /tmp/myfifo
then yourprogram < /tmp/myfifo
and, in another terminal, echo hello > /tmp/myfifo
Fundamentally, the problem is that you're mixing buffered stdio streams with low-level I/O. The reason the select
is blocking is because the previously typed data has already been read and is buffered in stdin
's stream data buffer. Try setting stdin
to unbuffered mode by calling setbuf(stdin, NULL)
.