Как контролировать порядок создания fork ()? - PullRequest
0 голосов
/ 28 апреля 2020

Задача состоит в том, чтобы создать дерево процессов, используя C fork () в Linux в следующем алфавитном порядке:

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

Требуемое дерево процессов

В настоящее время, используя if, я мог видеть abcdeGFi, наблюдая PID в htop, а не в правильном порядке.

Результаты, наблюдаемые с использованием htop

Видение * PID 1020 * (в настоящее время) всегда PID B + 1, поэтому я попытался выполнить патч, ОСТАНОВИТЬ C перед разветвлением B и ПРОДОЛЖИТЬ C впоследствии:

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

Это дает правильный порядок, однако, уродлив и подвержен ошибкам, если C не находится рядом с B, есть ли безупречный способ создания процессов в таком порядке?

Ответы [ 2 ]

2 голосов
/ 28 апреля 2020

Если я правильно понимаю.

  • Вы хотите предотвратить 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 никогда не будет создано (из-за cra sh или чего-то еще).

Таким образом, этот подход является "cra sh -защищенным", что означает, что у вас не будет процессов, ожидающих вечно, если что-то не так. И если вы захотите, вы можете даже различить guish обстоятельства, передав 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 {
   $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 голосов
/ 29 апреля 2020

Я одобряю анализ икегами необходимых пунктов удержания и передачи информации. Вот пример (без проверки ошибок) с использованием семафоров в 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
}
...