The problem is to create a tree of processes using C's fork() on Linux in this alphabetical order:
A: B, C, D
-B: E, F
-C: G
--G: I
-D:
Currently, by using if, I could see abcdeGFi by observing PIDs in htop, not the correct order.
Seeing C's PID is (currently) always B's PID + 1, so I tried to patch by STOP-ing C before forking B and CONT-ing C afterwards:
int b = getpid();
kill(b + 1, SIGSTOP);
fork(); /* E created */
if (getpid() == b) {
fork(); /* F created */
}
kill(b + 1, SIGCONT);
This gives the right order, however, it is ugly and prone to error if C isn't next to B, is there a flawless way to create processes in that order?
If I understand correctly.
That means
The word message is used very loosely here. I mean some form of information transfer.
A pipe could be used effectively here. Say that R (receiver) needs to wait for S (sender) to be created.
There are two circumstances under which R will proceed:
So, this approach is "crash-proof", meaning you won't have processes waiting forever if things go wrong. And if you so desired, you could even distinguish the circumstances by having S send a byte before closing the pipe. Perfect.
It just becomes a question of closing the pipes handles at the right time. The following is a demonstration of your goal achieved. (It's written in Perl, but pipe
, fork
, waitpid
, sleep
and close
are just thin wrappers for the C functions with the same name. Just ignore the $
.)
#!/usr/bin/perl
use strict;
use warnings;
use feature qw( say );
sub fork_child {
my $sub = shift;
my $pid = fork();
if (!$pid) {
if (!eval { $sub->(@_); 1 }) {
warn( eval { "$@" } // "Unknown error" );
exit(($? >> 8) || $! || 255);
}
exit(0);
}
return $pid;
}
sub a {
$0 = "a";
say "$0 is pid $$";
pipe(my $d_created_recver, my $d_created_sender);
pipe(my $f_created_recver, my $f_created_sender);
my $pid_b = fork_child(\&b, $d_created_recver, $d_created_sender, $f_created_recver, $f_created_sender);
my $pid_c = fork_child(\&c, $d_created_recver, $d_created_sender, $f_created_recver, $f_created_sender);
my $pid_d = fork_child(\&d, $d_created_recver, $d_created_sender, $f_created_recver, $f_created_sender);
close($_) for $d_created_recver, $d_created_sender, $f_created_recver, $f_created_sender;
waitpid($pid_b, 0);
waitpid($pid_c, 0);
waitpid($pid_d, 0);
}
sub b {
my ($d_created_recver, $d_created_sender, $f_created_recver, $f_created_sender) = @_;
$0 = "b";
say "$0 is pid $$";
# Not related to B or its descendants.
close($_) for $d_created_sender, $f_created_recver;
# Wait for D to be created.
read($d_created_recver, my $buf, 1);
close($d_created_recver);
my $pid_e = fork_child(\&e, $f_created_sender);
my $pid_f = fork_child(\&f, $f_created_sender);
# Allow G to be created.
close($f_created_sender);
waitpid($pid_e, 0);
waitpid($pid_f, 0);
}
sub c {
my ($d_created_recver, $d_created_sender, $f_created_recver, $f_created_sender) = @_;
$0 = "c";
say "$0 is pid $$";
# Not related to C or its descendants.
close($_) for $d_created_sender, $d_created_recver, $f_created_sender;
# Wait for F to be created.
read($f_created_recver, my $buf, 1);
close($f_created_recver);
my $pid_g = fork_child(\&g);
waitpid($pid_g, 0);
}
sub d {
my ($d_created_recver, $d_created_sender, $f_created_recver, $f_created_sender) = @_;
$0 = "d";
say "$0 is pid $$";
# Not related to D or its descendants.
close($_) for $d_created_recver, $f_created_sender, $f_created_recver;
# Allow E to be created.
close($d_created_sender);
sleep();
}
sub e {
my ($f_created_sender) = @_;
$0 = "e";
say "$0 is pid $$";
# Not related to E process or its decendants.
close($f_created_sender);
sleep();
}
sub f {
my ($f_created_sender) = @_;
$0 = "f";
say "$0 is pid $$";
# Allow G to be created.
close($f_created_sender);
sleep();
}
sub g {
$0 = "g";
say "$0 is pid $$";
my $pid_i = fork_child(\&i);
waitpid($pid_i, 0);
}
sub i {
$0 = "i";
say "$0 is pid $$";
sleep();
}
a();
Output: