Anselmo-Ed@cs.yale.edu (Ed Anselmo) (07/17/90)
One of the CS classes here used the following program as a simple example of creating a chain of processes. The idea was to watch the exit status of all the child processes and print the results from the parent process. This all works fine if the last process in the chain consumes all of stdin, e.g. pipe cat tail < /etc/termcap But if the last process doesn't consume stdin, e.g. pipe cat head < /etc/termcap it looks like the "head" process terminates, the "cat" process is waiting for the pipe to unblock, and parent process has gotten a SIGCHLD. Basically, things just sit there until you kill the parent process. Obviously, this doesn't happen if you try something similar with /bin/sh or /bin/csh. So how do /bin/sh and /bin/csh do it? I looked through the sources to sh and csh and got a bit lost .... /* compile with cc -o pipe pipc.c */ main (int argc, char *argv[]) { int pid, status, fd[2], i,j; struct entry {int pid, status;} *table; if (argc < 2) { printf ("Usage: pipe filter1 filter2 ... filterN\n"); exit(0); } table = (struct entry *) malloc (argc*sizeof(struct entry)); for (i = 2; i < argc; i++) { /* Create chain of processes */ pipe (fd); if ((pid = fork()) < 0) errorExit(-1); else if (pid == 0) { /* Child process */ close (fd[0]); /* stdout to parent */ if (fd[1] != 1) { dup2 (fd[1], 1); close (fd[1]); } execlp (argv[i-1], argv[i-1],0); /* Overlay by (i-1)st filter */ errorExit(-1); } else { /* Parent process */ table[i-1].pid = pid; /* Save child pid */ close (fd[1]); if (fd[0] != 0) { /* stdin from child */ dup2 (fd[0], 0); close (fd[0]); } } } if ((pid = fork()) < 0) /* Create last process in chain */ errorExit(-1); else if (pid == 0) { /* Child process */ execlp(argv[argc-1],argv[argc-1],0); /* Overlay by last filter */ errorExit(-1); } table[argc-1].pid = pid; /* Save child pid */ for (i = 1; i < argc; i++) { /* Wait for all children to die */ pid = wait(&status); for (j = 1; table[j].pid != pid; j++) ; table[j].status = status; } for (i = 1; i < argc; i++) { /* Print information */ printf ("%-10s pid=%d high=%d low=%d\n", argv[i], table[i].pid, (table[i].status>>8)&0377, table[i].status&0377); } } errorExit(int status) { perror("pipe"); exit(status); } -- Ed Anselmo anselmo-ed@cs.yale.edu {harvard,decvax}!yale!anselmo-ed
maart@cs.vu.nl (Maarten Litmaath) (07/17/90)
In article <ANSELMO-ED.90Jul16174646@bigbird.cs.yale.edu>,
Anselmo-Ed@cs.yale.edu (Ed Anselmo) shows a program containing various bugs.
)...
) for (i = 2; i < argc; i++) { /* Create chain of processes */
) pipe (fd);
) if ((pid = fork()) < 0)
) errorExit(-1);
) else if (pid == 0) { /* Child process */
) close (fd[0]); /* stdout to parent */
) if (fd[1] != 1) {
) dup2 (fd[1], 1);
) close (fd[1]);
) }
`fd[1]' must ALWAYS be closed, even if fd[1] == 1.
) execlp (argv[i-1], argv[i-1],0); /* Overlay by (i-1)st filter */
) errorExit(-1);
) }
) else { /* Parent process */
) table[i-1].pid = pid; /* Save child pid */
) close (fd[1]);
) if (fd[0] != 0) { /* stdin from child */
) dup2 (fd[0], 0);
) close (fd[0]);
) }
`fd[0]' must always be closed as well.
) }
) }
)
) if ((pid = fork()) < 0) /* Create last process in chain */
) errorExit(-1);
) else if (pid == 0) { /* Child process */
) execlp(argv[argc-1],argv[argc-1],0); /* Overlay by last filter */
) errorExit(-1);
) }
Add (in parent):
close(0);
) table[argc-1].pid = pid; /* Save child pid */
)
) for (i = 1; i < argc; i++) { /* Wait for all children to die */
) pid = wait(&status);
) for (j = 1; table[j].pid != pid; j++)
) ;
) table[j].status = status;
`pid' is NOT guaranteed to be in the table! (Question: what could have
happened?) So change the code to something like this:
for (j = 1; j < argc && table[j].pid != pid; j++)
;
if (j == argc)
i--; /* hack */
else
table[j].status = status;
) }
)...
--
"and with a sudden plop it lands on usenet. what is it? omigosh, it must[...]
be a new user! quick kill it before it multiplies!" (Loren J. Miller)