如何控制fork()的创建顺序?
How to control the creation order of fork()?
问题是使用 C 的 fork() 在 Linux 上按以下字母顺序创建进程树:
A: B, C, D
-B: E, F
-C: G
--G: I
-D:
Required tree of processes
目前,通过使用 if,我可以通过观察 htop 中的 PID 来看到 abcdeGFi,而不是正确的顺序。
Results observed using htop
看到 C 的 PID(当前)总是 B 的 PID + 1,所以我尝试通过在分叉 B 之前停止 C 并在之后继续 C 来打补丁:
int b = getpid();
kill(b + 1, SIGSTOP);
fork(); /* E created */
if (getpid() == b) {
fork(); /* F created */
}
kill(b + 1, SIGCONT);
这给出了正确的顺序,但是,如果 C 不在 B 旁边,它很难看并且容易出错,有没有一种完美的方法可以按该顺序创建进程?
如果我没理解错的话
- 您想阻止 B 创建 E 和 F 直到 D 由 A.
创建
- 您想阻止 C 创建 G 直到 F 被 B.
也就是说
- B 必须等待来自 D 或 A 的消息(表示 D 是在创建 E 和 F.
之前创建的)
- C 必须等待来自 F 或 B 的消息(表示 F 已创建)在创建 G.
之前
消息这个词在这里使用得非常松散。我的意思是某种形式的信息传输。
在这里可以有效地使用管道。假设R(接收方)需要等待S(发送方)被创建。
- 让S关闭管道的写入端。
- 让 R 在继续之前等待 EOF。
R会在两种情况下进行:
- S 已创建。
- S 将永远不会被创建(因为崩溃或其他原因)。
因此,这种方法是 "crash-proof",这意味着如果出现问题,您不会让进程永远等待。如果您愿意,您甚至可以通过让 S 在关闭管道之前发送一个字节来区分情况。完美。
这只是在正确的时间关闭管道句柄的问题。以下是您实现目标的演示。 (它是用 Perl 编写的,但是 pipe
、fork
、waitpid
、sleep
和 close
只是同名 C 函数的简单包装。只是忽略 $
.)
#!/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 {
[=10=] = "a";
say "[=10=] 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) = @_;
[=10=] = "b";
say "[=10=] 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) = @_;
[=10=] = "c";
say "[=10=] 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) = @_;
[=10=] = "d";
say "[=10=] 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) = @_;
[=10=] = "e";
say "[=10=] is pid $$";
# Not related to E process or its decendants.
close($f_created_sender);
sleep();
}
sub f {
my ($f_created_sender) = @_;
[=10=] = "f";
say "[=10=] is pid $$";
# Allow G to be created.
close($f_created_sender);
sleep();
}
sub g {
[=10=] = "g";
say "[=10=] is pid $$";
my $pid_i = fork_child(\&i);
waitpid($pid_i, 0);
}
sub i {
[=10=] = "i";
say "[=10=] is pid $$";
sleep();
}
a();
输出:
赞成ikegami对所需持有点和信息传递的分析。这是一个在 C:
中使用信号量的示例(省略错误检查)
#include <unistd.h>
#include <sys/sem.h>
int main()
{ // Create set of 2 semaphores; Linux initializes the values to 0.
int ss = semget(IPC_PRIVATE, 2, 0600);
pid_t B, C, D, E, F, G, I; // the child processes
if ((B = fork()) == 0)
{ // wait for semaphore number 0 (D)
semop(ss, &(struct sembuf){.sem_num=0, .sem_op=-1}, 1);
if ((E = fork()) == 0) return sleep(9); // E stay for a while
if ((F = fork()) == 0)
{ // C may now start G - unlock semaphore number 1
semop(ss, &(struct sembuf){.sem_num=1, .sem_op=+1}, 1);
return sleep(9); // F stay for a while
}
return sleep(9); // B stay for a while
}
if ((C = fork()) == 0)
{ // wait for semaphore number 1 (F)
semop(ss, &(struct sembuf){.sem_num=1, .sem_op=-1}, 1);
if ((G = fork()) == 0)
{
if ((I = fork()) == 0) return sleep(9); // I stay for a while
return sleep(9); // G stay for a while
}
return sleep(9); // C stay for a while
}
if ((D = fork()) == 0)
{ // B may now start E, F - unlock semaphore number 0
semop(ss, &(struct sembuf){.sem_num=0, .sem_op=+1}, 1);
return sleep(9); // D stay for a while
}
sleep(9); // A stay for a while
semctl(ss, 0, IPC_RMID); // remove the semaphore set
}
问题是使用 C 的 fork() 在 Linux 上按以下字母顺序创建进程树:
A: B, C, D
-B: E, F
-C: G
--G: I
-D:
Required tree of processes
目前,通过使用 if,我可以通过观察 htop 中的 PID 来看到 abcdeGFi,而不是正确的顺序。
Results observed using htop
看到 C 的 PID(当前)总是 B 的 PID + 1,所以我尝试通过在分叉 B 之前停止 C 并在之后继续 C 来打补丁:
int b = getpid();
kill(b + 1, SIGSTOP);
fork(); /* E created */
if (getpid() == b) {
fork(); /* F created */
}
kill(b + 1, SIGCONT);
这给出了正确的顺序,但是,如果 C 不在 B 旁边,它很难看并且容易出错,有没有一种完美的方法可以按该顺序创建进程?
如果我没理解错的话
- 您想阻止 B 创建 E 和 F 直到 D 由 A. 创建
- 您想阻止 C 创建 G 直到 F 被 B.
也就是说
- B 必须等待来自 D 或 A 的消息(表示 D 是在创建 E 和 F. 之前创建的)
- C 必须等待来自 F 或 B 的消息(表示 F 已创建)在创建 G. 之前
消息这个词在这里使用得非常松散。我的意思是某种形式的信息传输。
在这里可以有效地使用管道。假设R(接收方)需要等待S(发送方)被创建。
- 让S关闭管道的写入端。
- 让 R 在继续之前等待 EOF。
R会在两种情况下进行:
- S 已创建。
- S 将永远不会被创建(因为崩溃或其他原因)。
因此,这种方法是 "crash-proof",这意味着如果出现问题,您不会让进程永远等待。如果您愿意,您甚至可以通过让 S 在关闭管道之前发送一个字节来区分情况。完美。
这只是在正确的时间关闭管道句柄的问题。以下是您实现目标的演示。 (它是用 Perl 编写的,但是 pipe
、fork
、waitpid
、sleep
和 close
只是同名 C 函数的简单包装。只是忽略 $
.)
#!/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 {
[=10=] = "a";
say "[=10=] 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) = @_;
[=10=] = "b";
say "[=10=] 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) = @_;
[=10=] = "c";
say "[=10=] 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) = @_;
[=10=] = "d";
say "[=10=] 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) = @_;
[=10=] = "e";
say "[=10=] is pid $$";
# Not related to E process or its decendants.
close($f_created_sender);
sleep();
}
sub f {
my ($f_created_sender) = @_;
[=10=] = "f";
say "[=10=] is pid $$";
# Allow G to be created.
close($f_created_sender);
sleep();
}
sub g {
[=10=] = "g";
say "[=10=] is pid $$";
my $pid_i = fork_child(\&i);
waitpid($pid_i, 0);
}
sub i {
[=10=] = "i";
say "[=10=] is pid $$";
sleep();
}
a();
输出:
赞成ikegami对所需持有点和信息传递的分析。这是一个在 C:
中使用信号量的示例(省略错误检查)#include <unistd.h>
#include <sys/sem.h>
int main()
{ // Create set of 2 semaphores; Linux initializes the values to 0.
int ss = semget(IPC_PRIVATE, 2, 0600);
pid_t B, C, D, E, F, G, I; // the child processes
if ((B = fork()) == 0)
{ // wait for semaphore number 0 (D)
semop(ss, &(struct sembuf){.sem_num=0, .sem_op=-1}, 1);
if ((E = fork()) == 0) return sleep(9); // E stay for a while
if ((F = fork()) == 0)
{ // C may now start G - unlock semaphore number 1
semop(ss, &(struct sembuf){.sem_num=1, .sem_op=+1}, 1);
return sleep(9); // F stay for a while
}
return sleep(9); // B stay for a while
}
if ((C = fork()) == 0)
{ // wait for semaphore number 1 (F)
semop(ss, &(struct sembuf){.sem_num=1, .sem_op=-1}, 1);
if ((G = fork()) == 0)
{
if ((I = fork()) == 0) return sleep(9); // I stay for a while
return sleep(9); // G stay for a while
}
return sleep(9); // C stay for a while
}
if ((D = fork()) == 0)
{ // B may now start E, F - unlock semaphore number 0
semop(ss, &(struct sembuf){.sem_num=0, .sem_op=+1}, 1);
return sleep(9); // D stay for a while
}
sleep(9); // A stay for a while
semctl(ss, 0, IPC_RMID); // remove the semaphore set
}