Дочерняя цепочка Fork()

#c #fork #wait

#c #разветвление #подождите

Вопрос:

У меня есть следующий код, который создает определенное количество дочерних потоков с помощью fork() :

 #include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>

#define NUM_THREADS     4

int main()
{
        int i;
        pid_t pid;
        for(i = 0; i < NUM_THREADS; i  ){ //Do this equal to the number of threads
            pid = fork();//Use this to avoid multiple forking

            if(pid == 0){ //If it's a child process
                    printf("Hello World! Greetings from PID: %ld! :Dn", (long)getpid()); //getpid returns the pid of the process
                    exit(0); //Exit the process
            }else if(pid == -1){
                    printf("Oh no! Could not fork! :( Exiting!n");
                    return 0;
            }
    }
    int status;
    for(i = 0; i < NUM_THREADS; i  ){
            wait(amp;status);//wait until all the children processes have finished.
    }
    printf("All done! I am the parent process! My PID is: %ld if you were curious! n", (long)getpid());

    return 0;
  

}

Это было предоставлено нам в качестве примера. Его вывод выглядит так:

Привет, мир! Приветствую от PID: 118358! 😀
Привет, мир! Приветствую от PID: 118359! 😀
Привет, мир! Приветствую от PID: 118360! 😀
Привет, мир! Приветствую от PID: 118362! 😀

Что я хотел бы сделать, вместо того, чтобы иметь 1 родительский процесс со многими дочерними процессами, это заставить родительский процесс создать дочерний процесс, который создает дочерний процесс, и так далее для определенного количества потоков. Как я могу это сделать?

Комментарии:

1. Вы пытались закодировать это самостоятельно? С чем именно у вас возникли проблемы?

Ответ №1:

В вашем примере кода используется возвращаемое значение fork() , чтобы различать родительскую и дочернюю. Он продолжает выполнять итерации в родительском (предполагая, что fork() не сообщает об ошибке), и он exit() выполняется без итерации (после выдачи некоторого вывода) в дочернем. Это совершенно обычная процедура.

То, что вы предлагаете сделать, не сильно отличается; то, что вы описываете, в основном просто меняет роли родителя и потомка. Действительно, у вас есть более простая работа, потому что каждому родителю нужно только wait() для одного дочернего элемента. Это может быть сделано либо внутри цикла, либо снаружи, при условии только, что если последние дочерние wait() элементы, то он должен ожидать, что вызов укажет на ошибку из-за того, что у этого дочернего элемента нет собственных дочерних элементов. Однако, если родительский элемент этого не делает exit() или return изнутри цикла, тогда ему нужно прерваться, а не повторять дальше.

Ответ №2:

Эта задача аналогична преобразованию итеративного решения в рекурсивное решение. Первым шагом является удаление for цикла. А затем измените дочерний код так, чтобы каждый дочерний код:

  1. печатает свое сообщение
  2. обновляет переменную count, и если количество меньше максимального
  3. создает новый дочерний элемент
  4. ожидает своего дочернего элемента

Ключ в том, что каждый дочерний элемент получает копию переменных своего родителя, поэтому, если дочерний элемент обновляет переменную count, а затем разветвляется, новый дочерний элемент увидит обновленный счетчик.

Комментарии:

1. Конечно, можно было бы реализовать желаемое поведение рекурсивно, но любой рекурсивный алгоритм может быть преобразован в итеративный (без изменения наблюдаемого поведения), и наоборот . Поэтому вопрос о том, является ли решение итеративным или рекурсивным, кажется отвлекающим маневром.

2. Ну, в более реалистичной программе, если дочерние элементы создаются как цепочка, вызов функций, вероятно, более естественен, чем цикл, потому что каждый процесс будет иметь значимую и различную цель.

Ответ №3:

Во-первых, термин «поток», который вы использовали, неуместен в этой ситуации. Fork означает «процесс создания», а не «потоки».

Вы можете использовать рекурсивное решение, но вы должны помнить, что каждый процесс, создающий процесс, должен ждать статуса возврата, чтобы избежать процесса-зомби. Без использования какой-либо общей переменной между процессами вы можете использовать счетчик одной переменной, который увеличивается при каждом разветвлении. Но как только переменная counter достигнет максимального значения, «самый молодой» процесс, я имею в виду последний созданный процесс, должен завершиться, и один за другим завершатся другие процессы.

Вот простой код, который отлично работает:

 #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define NUM_PROCESS 5

int counter = 0;

void child_func() 
{
    pid_t pid;

    if (counter < NUM_PROCESS) {

        counter  ;

        pid = fork();

        if (pid < 0) {

            printf("fork failed counter = %dn", counter);

        }
        else if (pid == 0) {

            printf("Hello world ! Greetings from pid %ldn", (long)getpid());

            if (counter == NUM_PROCESS) {
                exit(0);
            }
            else {
                child_func();
            }
        }
        else {

            long var  = pid;

            wait(amp;pid);

            printf("My pid is %ld and i am the parent of %ld child, i exitn", (long)getpid(), var);

            exit(0);
        }   
    }
}

int main(void) 
{

    pid_t pid;

    pid = fork();

    if (pid < 0) {

        printf("Fork failed %dn", counter);

        return -1;
    }

    if (pid == 0) {

        child_func();
    }
    else {

        wait(amp;pid);
    }

    return 0;
}
  

Смотрите вывод