0

This question is a follow-up of 'Why does select() say that stdout and stderr are ready for reading and stdin is ready for writing?' which got closed as a duplicate of 'Writing to stdin and reading from stdout (UNIX/LINUX/C Programming)'.

While that question explains why we can read from file descriptors meant for stdout and stderr, the current question is different. This question is concerned about why stdout and stderr are ready for reading in a program while entering input manually from terminal but not so while redirecting standard input to the program.

Here is the code to reproduce the behavior. The following program attempts to check if stdin, stdout and stderr is ready for reading.

#include <stdio.h>
#include <sys/select.h>

int main(void)
{
    struct timeval tv;
    fd_set fds;
    int fd;

    tv.tv_sec = 10;
    tv.tv_usec = 0;

    FD_ZERO(&fds);
    FD_SET(0, &fds);
    FD_SET(1, &fds);
    FD_SET(2, &fds);

    select(3, &fds, NULL, NULL, &tv);

    for (fd = 0; fd < 3; fd++) {
        if (FD_ISSET(fd, &fds)) {
            printf("fd %d ready\n", fd);
        }
    }

    printf("tv: %ld seconds %ld microseconds\n", tv.tv_sec, tv.tv_usec);
    return 0;
}

If I wait for about 2 seconds and enter some data in standard input, the select() call returns and this is the output.

$ gcc readselect.c && ./a.out 
hi
fd 0 ready
fd 1 ready
fd 2 ready
tv: 8 seconds 222132 microseconds

However, if I redirect standard input to my program, then the program shows that only stdin is ready for reading.

$ gcc readselect.c && (sleep 2; echo hi) | ./a.out
fd 0 ready
tv: 7 seconds 994713 microseconds

Why does the behavior change now? Why does the program no longer say that stdout and stderr are also ready for reading like it did in the earlier example?

Community
  • 1
  • 1
Lone Learner
  • 18,088
  • 20
  • 102
  • 200
  • That's just how the underlying driver happens to work ... on your system. Different systems with different drivers will work differently. In short, don't do that. – user3386109 Oct 13 '16 at 01:58

2 Answers2

0

When you don't redirect them, file descriptors 0, 1 and 2 are all your terminal. And you can read from or write to any of them - try it! (Strictly speaking, the file descriptor is not the terminal, it just refers to the terminal)

When you type stuff in your terminal, your terminal is ready for reading, so all FDs 0, 1 and 2 are ready for reading because they are all the same terminal that you typed stuff in.

When you redirect input (file descriptor 0), FD 0 instantly becomes ready for reading even if you don't type stuff, because it's not waiting for you to type stuff. So your program sees that FD 0 is ready, reads FD 0, then exits. FD 1 and 2 would become ready if you typed stuff while the program was running, but you never got a chance to do that because your program exited straight away.

user253751
  • 57,427
  • 7
  • 48
  • 90
0

My explanation is both similar to and different from that of immibis.

In the first example, you have file descriptors 0, 1, and 2 all connected to the terminal. As you discovered previously, it often works that stdin is writable and both stdout and stderr are readable when they're connected to your login terminal. Consequently, when you type the hi, all three descriptors become readable.

In the second example, the configuration is quite different. The standard input is the pipe which, after a couple of seconds, gets the hi from the echo command. Meanwhile, standard output and standard error are both the terminal, and there's nothing to read because you didn't type anything.

If you used the second example invocation but typed something before the 2 second sleep was up, you'd find that file descriptors 1 and 2 were readable but file descriptor 0 was not.

Note that on macOS Sierra (and Mac OS X before it), the time out value is not changed, whereas on Linux, it is altered to indicate how much time is left. The Linux man page for select() notes that both behaviours are valid according to the POSIX specification of select().

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278