Michele Bini wrote:
> The supposed bug can be easily shown with strace
It isn't a bug.
> $ echo|strace ./proggy;
> [...]
> select(1, [0], NULL, NULL, NULL) = 1 (in [0])
> read(0, "\n", 1) = 1
> select(1, [0], NULL, NULL, NULL) = 1 (in [0])
> read(0, "", 1) = 0
> select(1, [0], NULL, NULL, NULL) = 1 (in [0])
> read(0, "", 1) = 0
> select(1, [0], NULL, NULL, NULL) = 1 (in [0])
> read(0, "", 1) = 0
> [...]
>
> How can it be? select() returns 1 (as if fd 0 had
> some data pending, but read() returns 0 and doesn't
> read any byte)
When read() returns 0 it is indicating EOF.
Note that select() doesn't indicate that data is available: it
indicates that calling read() on the descriptor won't block.
> At the same time the process use almost 100% of
> the CPU time (as shown by 'top').
Yep. The process is in a `busy wait'. It should probably terminate the
loop when read() returns 0.
> This 'bug' however doesn't happen if I run
> $ strace ./proggy
In this case, stdin is connected to the terminal. If you type Ctrl-D
to generate an EOF, select() will return and the subsequent read()
will return 0. Subsequent calls to select() will then block, but every
time that you type Ctrl-D, select() will return and read() will return
0.
In the original case, echo will write a newline to its stdout, then
close the write side of the pipe. Subsequently, calls to read() on the
other end of the pipe will always return 0, and the calls to select()
will always return immediately.
> Here is a little proggy to reproduce it:
>
> #include <sys/time.h>
> #include <sys/types.h>
> #include <unistd.h>
> void main() {
> for(;;) {
> char c;
> fd_set fdset;
> FD_ZERO(&fdset);
> FD_SET(0, &fdset);
> select(1, &fdset, NULL, NULL, NULL);
> read(0, &c, 1);
> } }
You should always check the return code from library functions. In
this case, your program should look something like:
int main(void)
{
for (;;)
{
char c;
fd_set fdset;
int n;
FD_ZERO(&fdset);
FD_SET(STDIN_FILENO, &fdset);
if (select(1, &fdset, NULL, NULL, NULL) < 0)
{
perror("select");
return 1;
}
if (!FD_ISSET(STDIN_FILENO, &fdset))
{
fprintf(stderr, "Something went wrong\n");
return 1;
}
n = read(STDIN_FILENO, &c, 1);
if (n < 0)
{
perror("read");
return 1;
}
if (!n)
break;
}
}
--
Glynn Clements <[EMAIL PROTECTED]>