如何控制fork()的创建顺序?

问题描述 投票:0回答:1

问题是在Linux上使用C的fork()按照这个字母顺序创建进程树。

A: B, C, D
-B: E, F
-C: G
--G: I
-D:

所需的进程树

目前,通过使用if,我可以通过观察htop中的PID看到abcdeGFi,而不是正确的顺序。

使用htop观察的结果

看到C的PID(目前)总是B的PID+1,所以我试着打了个补丁,在分叉B之前先STOP-C,之后CONT-C。

    int b = getpid();
    kill(b + 1, SIGSTOP);
    fork(); /* E created */
    if (getpid() == b) {
        fork(); /* F created */
    }
    kill(b + 1, SIGCONT);

这样就得到了正确的顺序,但是,如果C不在B的旁边,就很难看,而且容易出错,有没有一个完美的方法可以按照这个顺序创建进程?

c linux process fork pid
1个回答
2
投票

如果我的理解是正确的。

  • 你想防止 B 从创造 EF 直到 D 是由 A.
  • 你要防止 C 从创造 G 直到 F 是由 B.

这意味着

  • B 要等 DA (表示 D 创建),然后再创建 EF.
  • C 要等 FB (表示 F 创建),然后再创建 G.

信息这个词在这里用得很宽泛。我的意思是某种形式的信息传递。

这里可以有效地使用管道。比如说... R 需要等待 S (发件人)要创建。

  • S 关闭管道的写端。
  • R 等到发出EOF后再进行。

有两种情况: R 将进行。

  • S 创立了。
  • S 将永远不会被创建(因为崩溃或其他原因)。

所以,这种方法是 "防崩溃 "的,也就是说,如果出了问题,你不会让进程永远等待。如果你愿意的话,你甚至可以通过让 S 在关闭管道之前发送一个字节。很好。

这只是一个在正确的时间关闭管道手柄的问题。下面是你的目标实现的演示。(它是用Perl写的,但是 pipe, fork, waitpid, sleepclose 只是同名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 {
   $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


0
投票

我赞同ikegami对所需保持点和信息传输的分析。下面是一个在C语言中使用semaphores的例子(省略错误检查)。

#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
}
© www.soinside.com 2019 - 2024. All rights reserved.