pipe from child to parent: the parent exits, but the child does not

From: Alexander Farber (Alexander.Farber_at_gmail.com)
Date: 08/24/05


Date: 24 Aug 2005 02:31:55 -0700

Hi,

BACKGROUND (and the simple test case program is on the bottom):

I'm programming a load-balancer, which keeps fetching average
load values (similar to the rup command) from a list machines:

    status = clnt_call(rstat_clnt, RSTATPROC_STATS,
        (xdrproc_t) xdr_void, NULL,
        (xdrproc_t) xdr_statstime, (char *) &stats,
        timeout);
    ....

Because the clnt_call() blocks, I go through my list of machines,
create a pipe for each of them and fork a child, which calls that
function in a loop. Each child write()s then the 3 fetched int values
(ok, they are long on HP) via the pipe to the parent. And the parent
poll()s the reading ends of the pipes (which I also set nonblocking).

MY PROBLEM: is that when the parent exit()s, then the children don't
notice that the reading end of pipe is closed and keep running.

I understand, that as a workaround I could save the children pids
into my list of machines and - before exiting the parent -
go through that list and send a signal to each child.

But I was actually hoping, that the children would notice that event
also without a signal, since in the Stevens' Unix-Prg. book Ch. 15.2
he writes that when the SIGPIPE is ignored and the reading end of a
pipe closes, then the writing part gets -1 and errno = EPIPE.

So I have prepared a simple test case program, which forks NKIDS
number of children, who then keep writing to the parent. The parent
however sleeps 10 seconds and exits.

Now the strangs part is that for NKIDS = 1 I get the expected
behaviour - the single child gets -1 and EPIPE and exits:

    bolinux72:/home/afarber> ./kids-pipe
    written kid0, n = 5
    written kid0, n = 5
    written kid0, n = 5
    written kid0, n = 5
    written kid0, n = 5
    written kid0, n = 5
    written kid0, n = 5
    written kid0, n = 5
    written kid0, n = 5
    written kid0, n = 5
    bolinux72:/home/afarber> n = -1, errno = Broken pipe

But for NKIDS = 2 the children don't exit for some reason.

    bolinux72:/home/afarber> ./kids-pipe
    written kid0, n = 5
    written kid1, n = 5
    written kid0, n = 5
    written kid1, n = 5
    ....keeps going....

Since I have the same behaviour on OpenBSD 3.7 stable, RedHat Linux 9
and HP-UX 11.11, I'm probably missing something very obvious... ?

Regards
Alex

PS: And here is my test program (please change NKIDS there):

bolinux72:/home/afarber> cat kids-pipe.c

#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <stdio.h>
#include <sys/types.h>

extern char *__progname;

#define NKIDS 2
#define NAMELEN 5

struct kid_s {
        int pipefd[2];
        char name[NAMELEN];
} kids[NKIDS];

void
die(const char *s)
{
        perror(s);
        exit(1);
}

int
main(int argc, char *argv[])
{
        int i, n;
        pid_t pid;
        struct kid_s *pkid;

        if (signal(SIGPIPE, SIG_IGN) < 0)
                die("signal error");

        for (i = 0; i < NKIDS; i++) {
                pkid = &kids[i];

                snprintf(pkid->name, NAMELEN, "kid%d\n", i);

                if (pipe(pkid->pipefd) == -1)
                        die("pipe error");
        }

        for (i = 0; i < NKIDS; i++) {
                pkid = &kids[i];

                switch(pid = fork()) {
                case -1:
                        die("fork error");
                /* child, keep writing to parent */
                case 0:
                        close(pkid->pipefd[0]);
                        while(1) {
                                n = write(pkid->pipefd[1],
                                    pkid->name, NAMELEN);
                                if (n != NAMELEN) {
                                        fprintf(stderr,
                                            "n = %d, errno = %s\n",
                                            n, strerror(errno));
                                        break;
                                }
                                fprintf(stderr, "written %s, n = %d\n",

                                    pkid->name, n);
                                sleep(1);
                        }
                        _exit(0);
                /* parent, sleep and exit */
                default:
                        close(pkid->pipefd[1]);
                }
        }

        sleep(10);
        exit(0);
}



Relevant Pages

  • Re: continuous write to hard disk
    ... | herd" problem and the only cause for scheduling latency with the O ... Parent sets up some shared memory. ... Parent sets up pipes and forks child B. ... Child B blocks in readon pipe for message from parent. ...
    (comp.os.linux.development.system)
  • Re: Python, Tkinter and popen problem
    ... but that is not `both way': popen connects the parent to the child ... The pipe works one way: from the child to the parent ... A child process always inherits stdin, stdout and stderr from the parent ...
    (comp.lang.python)
  • Re: Forking
    ... On Wednesday 03 March 2004 20:47, Price, Jason generously enriched virtual ... pipe takes two filehandles: ... inherit filehandles of the parent process as a copy. ... waits for the child to teminate and returns its pid once it died. ...
    (perl.beginners)
  • Re: Questions about perl daemons with child processes and open files / signals
    ... but not when used in the parent? ... process the pipe in the child process. ... The parent sits in the waitpid, waiting for the child to exit. ...
    (comp.lang.perl.misc)
  • Unix Programming FAQ (v1.37)
    ... Why use _exit rather than exit in the child branch of a fork? ... Why doesn't my process get SIGHUP when its parent dies? ... How do I create a named pipe? ... How do I compare strings using regular expressions? ...
    (comp.unix.programmer)

Loading