jirislav
jirislav

Reputation: 359

C - How to detect EOF in pipe when reading only single char?

As explained in this answer, I'd be expecting the reader process to catch the EOF right after the writer process closes all related file descriptors.

But that doesn't happen and this program ends up stuck in an endless loop. Parent waits for it's child to finish & child waits for EOF signalizing closed pipe.

Why the reader process doesn't receive EOF?

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <wait.h>

#define STRING_TO_SEND "Hello, world!\n"

int main() {
    int fd[2], i = 0;
    __pid_t pid;
    char _char;
    ssize_t nbytes;

    pipe(fd);
    pid = fork();

    if (pid == -1) {
        // Error
        perror("Error forking!");
        return EXIT_FAILURE;

    } else if (pid == 0) {

        // Child
        close(fd[1]);
        while ((nbytes = read(fd[0], &_char, 1)) != EOF) {
            if (nbytes == 0)
                continue;
            putchar(_char);
        }
        close(fd[0]);

    } else {

        // Parent
        close(fd[0]);

        for(;;) {
            _char = STRING_TO_SEND[i++];
            write(fd[1], &_char, 1);
            if (_char == '\0')
                break;
        }

        close(fd[1]);
        close(STDOUT_FILENO);

        while (wait(NULL)>0) {}
    }
    return 0;
}

Upvotes: 0

Views: 5793

Answers (2)

Petr Skocik
Petr Skocik

Reputation: 60127

EOF is a constant usually defined to -1 that stdio (the C library buffering layer around the raw system calls) uses to signal end of file in functions like getchar() which conflate the returned character with an end-of-file signal.

read signals end-of-file by simply returning 0. Note that it can also return -1 if there's an error (e.g., you can get EINTR if the read is interrupted by a signal handler before it read anything).

Consequently, what you want is something like:

while ((nbytes = read(fd[0], &_char, 1)) > 0){ /*...*/ }
if (0>nread) { /*report error (or maybe repeat if it's EINTR)*/ }

Manpages (read(2)) or the POSIX spec for read document all this.

Upvotes: 1

P.P
P.P

Reputation: 121417

You simply misunderstood the "end of file" indication of read() which simply means nothing more to read for read() (read() returns 0 in that case). But read() doesn't actually return the value EOF. So your condition should be:

while ((nbytes = read(fd[0], &_char, 1)) > 0) {

Also __pid_t is an internal type of your C library. You shouldn't use that; just use pid_t.

See read(2)'s man page for details.

Upvotes: 7

Related Questions