From: subbarao@tex.Princeton.EDU (Kartik Subbarao)
Newsgroups: comp.unix.wizards
Subject: Re: fork() and forget..How?
Date: 8 Oct 92 15:35:13 GMT
Reply-To: subbarao@fc.hp.com
Organization: putchar('I'); for (i = 0; i < 3; i++) putchar('E');

In article <1992Oct07.194020.14527@ctms.gwinnett.com> kirk@ctms.gwinnett.com (R. Kirk Waterhouse) writes:

>Wow!, did I say that right?  Yea, see picture below.
>
>-----------     P      ----------- fork()---> Channel 3 }  These are not so much
>|  Main   | --->I      |   My    | fork()---> Channel 2 }  channels, as they are
>| Process |     P ---> | Process | fork()---> Channel 4 }  independtly running
>-----------     E      ----------- fork()---> Channel 1 }  processes that die
>                                   fork()---> Channel x }  when complete.

I still don't completely understand your application, but your picture does
a reasonable job of explaining it. For instance, I think your text
explaining the picture had data going in both directions. Anyway, I'll
assume that your picture is what you intended.
>
>I followed the examples in the AT&T Programmers Guide and other books and they 
>won't quite work.
>
>   if (fork()) {
>      if(child) {
>         ...
>         process_data_and_die();
>         exit(1);
>      }
>      else
>      if (parent) {
>         wait()
>      }
>   }
>
>If I do it basicly like this I only get one instantiation of my child process
>because the parent sits there at the wait, losing data from it's input because
>this child takes approx. 1-2 minutes to complete.
>
>If I leave out the wait, I get hundreds of instantiations of my child process
>which become zombie processes with a parent of (1 or init) until I can issue
>a wait().  (When could I issue a wait()?)  At this point I loose data again from
>my input process until all of these zombie processes get cleared up. (If I do 
>not issue the wait or kill my process, I soon cannot fork() anymore, I guess I 
>am exceeding the maxixmum number of processes) 
>
>Any body got any ideas? This is the first time I have used fork(), is there
>something basic I am missing?

Processes generate SIGCHLD when they die. What you want here is to install a 
signal handler that waits for the children as and when they die:

# include <signal.h>

waiter()
{
	union wait st;
	while (wait3(&st, WNOHANG, NULL) > 0);
}

in your process that fork()s, stick this in:

signal(SIGCHLD, waiter);

So, you need not insert any wait's in your main code, and you can service 
requests from the pipe without waiting for the previous one to finish. When
the processes die, the interrupt routine waiter() will be called to clean
up shop, and you'll be returned back to your regularly scheduled
programming.

>At this point (until I figure out the fork()'s) I am just running my child 
>process in the background and opening up an IPC FIFO between us and having to 
>know in advance how many channels I will need so I can open up that many FIFOs.
>
>This works, but there seems to be alot more overhead involved with the FIFOs
>than would be with the fork()'s. The fork() would also be alot more flexible
>since it would allow as many channels as needed, and I could regulate how many
>would run as a maximum rather than having to run x number of background
>processes and open x number of FIFOs.

Yup, this is the alternative approach. :-) I would tend to think that there is 
more overhead with creating a brand new process with fork() than in reserving
space to transfer data. As to knowing exactly how many requests in advance,
that is not an big problem here. Since each pipe only requires an int's
worth of bookkeeping, you cuold simply ignore the problem and create, say,
int in[100], out[100]. This allows you a max of 500 pipes, and only takes
up 800 bytes of space (assuming 4-bye ints). If you're a stickler for space, 
use malloc() and dynamically create new pipes.

Although I tend to be partial to the multiple-pipe approach, one of the 
disadvantages is that you have to do your own multiplexing between them,
with select() (or poll() perhaps in your case since you mentioned SYSV),
asynchronous I/O, or some other mechanism. UNIX takes care of the
multiplexing in the multiple-process approach.

I guess also that one of my uncertainties comes up here in that I'm uncertain 
here about the direction of data transfer -- that is, whether you're
interested in only interested in a pipe (one way data transfer), rather
than a socket (bidirectional).

Of course, when people finally work out decent lightweight threads packages
that don't incur massive costs per fork(), then maybe this debate will be
moot.

The problem gets more interesting across a network. I could go on about this, 
it's an intriguing topic, but I'll cut it short this time. :-) Hope this
helps.

	-Kartik