I'm having trouble understanding pipe
and fork
, at least implementing it in practice. I would like to create n
children each with their own pipe.
I thought of doing something like:
int main(void) {
int fd[2];
for (int i = 0; i < n; i++) {
pipe(fd);
r = fork();
if (r == 0) {
// do child stuff
} else if (r > 0) {
// do parent stuff
}
}
}
But doing this would result in the children making processes of their own which isn't what I want.
Moreover, how would you make it so that the parent and child run concurrently, where the child is constantly writing to the pipe and the parent, with access to the pipe to each child, reads from it, does something with it, then discards it so that the child can write something new to the pipe?
You're going to need an array of file descriptors big enough for one pair of file descriptors for each child.
The parent code will create the pipes. Each child processes should close the pipes of any sibling processes. The exact mechanics of that depend on how you create the pipes; there are multiple workable options.
The if (r == 0)
code will execute the child process and should ensure it exits rather than continuing the loop.
The parent process will simply continue creating the other child processes before going into its own processing loop.
You're going to need to decide how to determine which children have written data so that the parent can read it without blocking. You might use select()
or poll()
or a variant on those, or the parent might make the read ends of the pipe non-blocking, or …
In a comment you ask "…people have mentioned threading but is this possible with pipe?", to which the answer is "yes, it's possible — though it isn't clear that it is necessary or desirable".
Would you happen to have sample code doing what you've described?
It's probably simpler to write than to find. The be_childish()
function is responsible for doing whatever a child should do. It should not return. If it does, that will be reported as a failure.
You've not said much about what the child processes do, so it's hard to fill in the gaps there.
int main(void)
{
enum { NUM_CHILDREN = 5 };
int fd[NUM_CHILDREN][2];
for (int i = 0; i < NUM_CHILDREN; i++)
{
pipe(fd[i]);
int pid = fork();
if (pid < 0)
…error exit…
if (pid == 0)
{
// Child
// Close sibling pipes
for (int j = 0; j < i; j++)
{
close(fd[j][0]);
close(fd[j][1]);
}
close(fd[i][0]); // Close read end of pipe
be_childish(fd[i][1]);
exit(EXIT_FAILURE);
}
}
for (int i = 0; i < NUM_CHILDREN; i++)
close(fd[i][1]); // Close write end of every child's pipe
// setup complete, unless you need to make the read end of the pipes non-blocking
// do parental stuff, reading from the various child pipes
for (int i = 0; i < NUM_CHILDREN; i++)
close(fd[i][0]); // Close read end of every child's pipe
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
printf("Child %d exited with status 0x%.4X\n", corpse, status);
return 0;
}
Beware: this code has not been near a compiler, much less run. There are probably bugs in it.