Как вернуть управление родителю после запуска нескольких подпроцессов, не дожидаясь их выхода ()? - PullRequest
0 голосов
/ 09 июня 2018

Я пытаюсь создать дерево процессов, структурированных в двух измерениях.На самом деле будут только процессы, распространяющиеся вправо и вниз от родителя.Таким образом, массив выглядит так:

tree[0] = tree[1] = 2;
tree[2] = 4;
tree[3] = 1;

Может создать такую ​​структуру:

process_tree───process_tree───process_tree
           |───process_tree───process_tree
           |───process_tree───process_tree───process_tree───process_tree
           |───process_tree

Моя проблема на самом деле в том, что всякий раз, когда я создаю ветвь процессов, программа не возвращает исходный родительский PID,Пожалуйста, прочитайте ppid как «исходный идентификатор родителей» и учтите, что после запуска приведенного ниже кода программа застревает на while (getpid() != ppid) навсегда (spawn() просто создает и выполняет ветвь процессов)

while (branch < YDEPTH) {
  printf ("PARENT %d: Hey Kid!\n", getpid());
  spawn(tree[branch], ppid);
  ++ branch;
  while (getpid() != ppid)
    load();   
}

Вкл.По трассировке видно, что pid остается таким же, как и в предыдущем процессе

PARENT 8585: Hey Kid!
FALL 8585: My parent's PID = 13617
Z CHILD 8586: My parent's PID = 8585
FALL 8586: My parent's PID = 8585
Z CHILD 8587: My parent's PID = 8586
Out to Y dimension from 8587
Out to Y dimension from 8587
Wait for parent (8587/8585)[\]

Если кому-то может быть интересно, как именно работает spawn(), я выложил код здесь: https://paste.debian.net/1028623/. Да,Я знаю, что я не использую ppid там, использовал его для некоторых тестов:).

Ответы [ 2 ]

0 голосов
/ 10 июня 2018

Ответ на ваш первоначальный вопрос: возврата контроля нет.В этом нет необходимости, потому что родитель никогда не уступает контроль своему дочернему элементу - оба работают параллельно (приблизительно) и независимо (в основном).

Что они могут сделать:

  1. выйти из себя
  2. Попробуйте убить другой процесс
  3. обмениваться данными с другими процессами с помощью методов IPC

Вы говорите, что непосредственная проблема возникает прямо в

printf ("PARENT %d: Hey Kid!\n", getpid()); spawn(tree[branch], ppid); 
++ branch; while (getpid() != ppid)  load();

С циклом while, который никогда не заканчивается.

В цикле вы проверяете, что текущий процесс pid равен ppid.Предположим, что getpid() != ppid, поэтому цикл будет введен.Оказавшись внутри цикла, pid вашего процесса или значение ppid должны будут измениться, чтобы снова выйти из цикла.Код, который вы предоставляете, не меняет их напрямую.Есть 2 возможности снова выйти из цикла:

  1. ppid - глобальная переменная и load изменяет ее значение.
  2. load меняет pid текущий процесс.

Относительно пункта 2: Обычно процесс не может изменить свой pid - поэтому было бы удивительно, если бы цикл однажды ввел ever снова вышел.

Что происходит внутри вашей функции load?Что вы ожидаете, что произойдет?

0 голосов
/ 10 июня 2018

Вот реализация того, что, я думаю, вам нужно - с обильной печатью, которая показывает мне, что код делает то, что я ожидаю.Я использую некоторый мой код сообщения об ошибках, который очень помогает, имея опции для печати времени и идентификатора процесса в каждом сообщении.Этот код доступен в моем репозитории SOQ (Вопросы о переполнении стека) на GitHub в виде файлов stderr.c и stderr.h в подкаталоге src / libsoq .

#include "stderr.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

static void wait_for_kids(void);
static void random_nanosleep(void);

static void process_tree(int row_num, int num_kids)
{
    int i;
    int ppid = getpid();
    err_remark("Launching row %d (%d kids)\n", row_num, num_kids);
    for (i = 0; i < num_kids; i++)
    {
        int pid = fork();
        if (pid < 0)
            err_syserr("Failed to fork: ");
        if (pid == 0)
        {
            err_remark("Child %d at work (parent %d)\n", i + 1, (int)getppid());
        }
        else
        {
            err_remark("Child %d launched (PID %d)\n", i + 1, pid);
            break;
        }
    }
    wait_for_kids();
    if (getpid() != ppid)
    {
        random_nanosleep();
        int status = 10 * row_num + i + 1;
        err_report(ERR_EXIT | ERR_PID | ERR_MILLI | ERR_NOARG0, status,
                   "Process complete (status %d = 0x%.2X)\n", status, status);
    }
    err_remark("Launching of row %d complete\n", row_num);
}

int main(int argc, char **argv)
{
    err_setarg0(argv[0]);
    err_setlogopts(ERR_PID | ERR_MILLI | ERR_NOARG0);
    if (argc != 1)
        err_usage("");

    int proc_tree[] = { 3, 2, 4, 2 };
    enum { NUM_ROWS = sizeof(proc_tree) / sizeof(proc_tree[0]) };

    err_remark("At work!\n");

    for (int i = 0; i < NUM_ROWS; i++)
        process_tree(i + 1, proc_tree[i]);

    err_remark("All done!\n");
    return 0;
}

static void wait_for_kids(void)
{
    int corpse;
    int status;
    while ((corpse = wait(&status)) > 0)
        err_remark("Child %d exited with status 0x%.4X\n", corpse, status);
    err_remark("No progeny left - woe is me!\n");
}

static void random_nanosleep(void)
{
    srand(getpid());    /* Not very good, but adequate */
    struct timespec nap = { .tv_sec = 0, .tv_nsec = (rand() % 5000 + 5000) * 10000 };
    nanosleep(&nap, 0);
}

Основной операционный код в main() и process_tree();две другие функции, объявленные вверху, являются служебными функциями, которые выполняют полезную работу, но не являются основной частью ответа.

Код в process_tree() имеет дело с одной строкой массива и ему сообщаютномер строки (для целей отчетности) и количество детей.Он сообщает основную информацию о том, что он будет делать, а затем запускает цикл for, очень похожий на ваш.Процесс вилки;если происходит сбой, он сообщает о проблеме (включая системное сообщение об ошибке) и завершает работу.Если это ребенок, он говорит, что он на работе и кто его родитель, и переходит к следующей итерации цикла.Если это родитель, он отмечает, какой дочерний элемент запущен, и выходит из цикла.В конце цикла процесс (будь то исходный или один из дочерних процессов) ожидает смерти своих потомков, а затем, если это не исходный процесс, берет случайный сон и завершает работу с неНулевой статус (и печатное сообщение).Если это родительский (оригинальный) процесс, он говорит, что он завершен с текущей строкой и возвращает.

Код в main() в основном прост.Он выполняет минимальную обработку аргументов и настраивает систему отчетов об ошибках так, чтобы сообщения не включали имя программы (ERR_NOARG0), но включали время с миллисекундами (ERR_MILLI) и PID (ERR_PID), и сообщали, что она работает,Затем для каждой строки дерева вызывается process_tree().затем он сообщает «все выполнено» и завершается.

Функция wait_for_kids() просто ожидает смерти дочерних процессов и сообщает о состоянии каждого дочернего элемента, который действительно умирает, затем объявляет свое бездетное состояние и возвращает.

Код random_nanosleep() инициализирует генератор случайных чисел отдельно для каждого процесса, чтобы он спал в течение различного времени, которое настроено на период от 50 до 100 миллисекунд.(Без этого весь процесс занимает около 20 миллисекунд.)

Пример выполнения:

2018-06-09 14:23:15.717 - pid=29868: At work!
2018-06-09 14:23:15.718 - pid=29868: Launching row 1 (3 kids)
2018-06-09 14:23:15.718 - pid=29868: Child 1 launched (PID 29869)
2018-06-09 14:23:15.718 - pid=29869: Child 1 at work (parent 29868)
2018-06-09 14:23:15.719 - pid=29869: Child 2 launched (PID 29870)
2018-06-09 14:23:15.719 - pid=29870: Child 2 at work (parent 29869)
2018-06-09 14:23:15.720 - pid=29870: Child 3 launched (PID 29871)
2018-06-09 14:23:15.720 - pid=29871: Child 3 at work (parent 29870)
2018-06-09 14:23:15.720 - pid=29871: No progeny left - woe is me!
2018-06-09 14:23:15.790 - pid=29871: Process complete (status 14 = 0x0E)
2018-06-09 14:23:15.790 - pid=29870: Child 29871 exited with status 0x0E00
2018-06-09 14:23:15.791 - pid=29870: No progeny left - woe is me!
2018-06-09 14:23:15.843 - pid=29870: Process complete (status 13 = 0x0D)
2018-06-09 14:23:15.844 - pid=29869: Child 29870 exited with status 0x0D00
2018-06-09 14:23:15.844 - pid=29869: No progeny left - woe is me!
2018-06-09 14:23:15.927 - pid=29869: Process complete (status 12 = 0x0C)
2018-06-09 14:23:15.928 - pid=29868: Child 29869 exited with status 0x0C00
2018-06-09 14:23:15.928 - pid=29868: No progeny left - woe is me!
2018-06-09 14:23:15.928 - pid=29868: Launching of row 1 complete
2018-06-09 14:23:15.928 - pid=29868: Launching row 2 (2 kids)
2018-06-09 14:23:15.929 - pid=29868: Child 1 launched (PID 29872)
2018-06-09 14:23:15.929 - pid=29872: Child 1 at work (parent 29868)
2018-06-09 14:23:15.930 - pid=29872: Child 2 launched (PID 29873)
2018-06-09 14:23:15.930 - pid=29873: Child 2 at work (parent 29872)
2018-06-09 14:23:15.931 - pid=29873: No progeny left - woe is me!
2018-06-09 14:23:15.987 - pid=29873: Process complete (status 23 = 0x17)
2018-06-09 14:23:15.988 - pid=29872: Child 29873 exited with status 0x1700
2018-06-09 14:23:15.988 - pid=29872: No progeny left - woe is me!
2018-06-09 14:23:16.076 - pid=29872: Process complete (status 22 = 0x16)
2018-06-09 14:23:16.076 - pid=29868: Child 29872 exited with status 0x1600
2018-06-09 14:23:16.076 - pid=29868: No progeny left - woe is me!
2018-06-09 14:23:16.076 - pid=29868: Launching of row 2 complete
2018-06-09 14:23:16.076 - pid=29868: Launching row 3 (4 kids)
2018-06-09 14:23:16.077 - pid=29868: Child 1 launched (PID 29874)
2018-06-09 14:23:16.077 - pid=29874: Child 1 at work (parent 29868)
2018-06-09 14:23:16.078 - pid=29874: Child 2 launched (PID 29875)
2018-06-09 14:23:16.078 - pid=29875: Child 2 at work (parent 29874)
2018-06-09 14:23:16.079 - pid=29875: Child 3 launched (PID 29876)
2018-06-09 14:23:16.079 - pid=29876: Child 3 at work (parent 29875)
2018-06-09 14:23:16.080 - pid=29876: Child 4 launched (PID 29877)
2018-06-09 14:23:16.080 - pid=29877: Child 4 at work (parent 29876)
2018-06-09 14:23:16.080 - pid=29877: No progeny left - woe is me!
2018-06-09 14:23:16.159 - pid=29877: Process complete (status 35 = 0x23)
2018-06-09 14:23:16.159 - pid=29876: Child 29877 exited with status 0x2300
2018-06-09 14:23:16.160 - pid=29876: No progeny left - woe is me!
2018-06-09 14:23:16.221 - pid=29876: Process complete (status 34 = 0x22)
2018-06-09 14:23:16.221 - pid=29875: Child 29876 exited with status 0x2200
2018-06-09 14:23:16.222 - pid=29875: No progeny left - woe is me!
2018-06-09 14:23:16.314 - pid=29875: Process complete (status 33 = 0x21)
2018-06-09 14:23:16.315 - pid=29874: Child 29875 exited with status 0x2100
2018-06-09 14:23:16.315 - pid=29874: No progeny left - woe is me!
2018-06-09 14:23:16.389 - pid=29874: Process complete (status 32 = 0x20)
2018-06-09 14:23:16.390 - pid=29868: Child 29874 exited with status 0x2000
2018-06-09 14:23:16.390 - pid=29868: No progeny left - woe is me!
2018-06-09 14:23:16.390 - pid=29868: Launching of row 3 complete
2018-06-09 14:23:16.390 - pid=29868: Launching row 4 (2 kids)
2018-06-09 14:23:16.391 - pid=29868: Child 1 launched (PID 29878)
2018-06-09 14:23:16.391 - pid=29878: Child 1 at work (parent 29868)
2018-06-09 14:23:16.392 - pid=29878: Child 2 launched (PID 29879)
2018-06-09 14:23:16.392 - pid=29879: Child 2 at work (parent 29878)
2018-06-09 14:23:16.393 - pid=29879: No progeny left - woe is me!
2018-06-09 14:23:16.457 - pid=29879: Process complete (status 43 = 0x2B)
2018-06-09 14:23:16.457 - pid=29878: Child 29879 exited with status 0x2B00
2018-06-09 14:23:16.457 - pid=29878: No progeny left - woe is me!
2018-06-09 14:23:16.554 - pid=29878: Process complete (status 42 = 0x2A)
2018-06-09 14:23:16.556 - pid=29868: Child 29878 exited with status 0x2A00
2018-06-09 14:23:16.556 - pid=29868: No progeny left - woe is me!
2018-06-09 14:23:16.556 - pid=29868: Launching of row 4 complete
2018-06-09 14:23:16.556 - pid=29868: All done!
...