Создание дерева процессов с использованием fork - PullRequest
0 голосов
/ 27 октября 2018

Я пытаюсь создать дерево процессов, показанное на рисунке. В основном, если уровень ровный, я хочу создать один дочерний процесс и завершить родительский процесс. Если уровень нечетный, я хочу создать два дочерних процесса, а затем завершить родительский процесс. Я написал программу прямо сейчас, но я думаю, что очень трудно представить, какое дерево процессов на самом деле создает моя программа. Я написал несколько комментариев к коду, чтобы объяснить, как я думал. Я также хочу вывести PID нижних дочерних элементов дерева, которые мой код выполняет неправильно.

enter image description here

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

int main(int argc, char *argv[]){ 
    pid_t pid, ppid; 
    int n, i;
    int childstate;
    int count = 0; 

    if(argc != 2){ 
        printf("Wrong number of arguments"); 
        exit(-1); 
    } 
    n = atoi(argv[1]); 

    fork(); //start process 0
    for(i = 1; i < n + 1; i++){ 
        if(i % 2 != 0){ 
            fork(); //if odd level start 1 child process
             if(getpid() == 0){
                kill (getppid(), 9); //terminate parent process
            }
        } else { 
            if(fork() > 0){  //start new process
                fork(); //if new process is not a child start another process
                if(getpid() == 0){
                    kill (getppid(), 9); //terminate parent process
                }
            } 
        } 
        if(i == n){ //print pid of leaves (not working correctly)
            printf("Process: %d \n", getpid()); 
        } 
    }
    return 0; 
}

Ответы [ 4 ]

0 голосов
/ 28 октября 2018

Я также хочу вывести PID нижних потомков дерева, который мой код делает неправильно.

Пусть ваши процессы выводят дерево на языке Dot и используютGraphviz для вывода дерева.

Например, если вы сохраните следующее как, скажем, tree.c :

#define  _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

int process(const unsigned int level, const unsigned int maxlevel, FILE *dot)
{
    int           status = EXIT_SUCCESS, childstatus;
    unsigned int  children, i;
    pid_t         p, child[2];

    if (dot) {
        /* Output a node for this child, */
        fprintf(dot, "    \"%ld\" [ label=\"Process %ld\" ];\n", (long)getpid(), (long)getpid());

        /* and if not at the top level (0), an edge from our parent. */
        if (level)
            fprintf(dot, "    \"%ld\" -> \"%ld\";\n", (long)getppid(), (long)getpid());

        fflush(dot);
    }

    /* No more forking? */
    if (level >= maxlevel) {
        if (level)
            exit(status);
        else
            return status;
    }

    /* Odd levels create two child processes, even one. */
    if (level & 1)
        children = 2;
    else
        children = 1;

    /* Fork the child processes, */
    for (i = 0; i < children; i++) {
        child[i] = fork();
        if (child[i] == -1) {
            fprintf(stderr, "Cannot fork: %s.\n", strerror(errno));
            exit(EXIT_FAILURE);
        } else
        if (!child[i]) {
            /* have each child run process() and nothing else, */
            exit(process(level + 1, maxlevel, dot));
        }
        /* This line is run in parent only. */
    }

    /* and wait for them. */
    for (i = 0; i < children; i++) {
        if (child[i] != -1) {
            do {
                p = waitpid(child[i], &childstatus, 0);
            } while (p == -1 && errno == EINTR);
            if (p != child[i])
                status = EXIT_FAILURE;
        } else
            status = EXIT_FAILURE;
    }

    if (level)
        exit(status);
    else
        return status;
}

int dot_process_tree(const int levels, FILE *out)
{
    int  retval = EXIT_SUCCESS;

    if (out) {
        fprintf(out, "digraph {\n");
        fflush(out);
    }

    if (levels > 0)
        retval = process(0, levels - 1, out);

    if (out) {
        fprintf(out, "}\n");
        fflush(out);
    }

    return retval;
}

int main(void)
{
    return dot_process_tree(5, stdout);
}

, скомпилируете и запустите его, используя

reset ; gcc -Wall -Wextra -O2 tree.c -o tree && ./tree | dot -Tx11

вы получите красивое графическое дерево процессов.(Используйте dot -Tsvg > out.svg или dot -Tpng > out.png, чтобы сохранить его как изображение SVG или PNG.) В моей системе:

example process tree

Обратите внимание, что нет причин, по которым идентификаторы процессадолжен быть в порядке дерева.Хотя, например, Linux передает их довольно упорядоченным образом, они могут быть в любом порядке, даже совершенно случайными.Поэтому не делайте никаких предположений относительно PID.

Сам язык Dot прост.Вывод этой программы выглядит примерно так:

digraph {
    "12375" [ label="Process 12375" ];
    "12377" [ label="Process 12377" ];
    "12375" -> "12377";
    "12378" [ label="Process 12378" ];
    "12377" -> "12378";
    "12379" [ label="Process 12379" ];
    "12377" -> "12379";
    "12380" [ label="Process 12380" ];
    "12378" -> "12380";
    "12381" [ label="Process 12381" ];
    "12379" -> "12381";
    "12382" [ label="Process 12382" ];
    "12380" -> "12382";
    "12384" [ label="Process 12384" ];
    "12381" -> "12384";
    "12383" [ label="Process 12383" ];
    "12380" -> "12383";
    "12385" [ label="Process 12385" ];
    "12381" -> "12385";
}

, что должно быть очевидно;узлы именуются по идентификатору процесса, а [ label="Title" ] устанавливает текст в узле.Это не тот же прогон, что и на схеме выше, поэтому идентификаторы процессов различаются.

В Dot цифры нужно заключать в кавычки, если они используются в качестве имени, но если имя начинается с буквы, вы нене нужно цитировать это.См. Документация Graphviz для получения более подробной информации.(Страница Node, Edge и Graph Attributes - это та страница, которая вам обычно нужна.)

Если вы хотите отображать уровень в каждом узле, используйте

        fprintf(dot, "    \"%ld\" [ label=\"Process %ld, level %u\" ];\n", (long)getpid(), (long)getpid(), level + 1);

вprocess().(Используется уровень 0 вперед, причем все ненулевые уровни являются дочерними процессами, а уровень 0 - исходным процессом. Вот почему возвращается уровень 0 и все остальные уровни exit().)

0 голосов
/ 27 октября 2018
fork(); //if odd level start 1 child process
if (getpid() == 0){
    kill (getppid(), 9); //terminate parent process
}

Эта логика неверна: getpid() не возвращает 0 / fork не возвращает pid в дочернем процессе - он просто возвращает 0, чтобы показать, что это дочерний процесспроцесс - он может узнать pid родителя, позвонив getpid раньше.

Логика должна быть:

pid_t child = fork();
if (child > 0) {
    // use exit instead of kill! exit terminates this process
    exit(0);
}
if (child < 0) {
    ... an error occurred in fork ...
}
0 голосов
/ 27 октября 2018

getpid никогда не может быть нулем. Как я уже упоминал в моих главных комментариях, вы хотите, чтобы родитель ждал детей, а не наоборот и слишком много вилок.

Вот исправленная версия, которая, я думаю, работает:

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

int
main(int argc, char *argv[])
{
    pid_t pid;
    pid_t ppid;
    int i;
    int n;
    int pcur;
    int pcnt;

    if (argc != 2) {
        printf("Wrong number of arguments");
        exit(-1);
    }
    n = atoi(argv[1]);

    pid = fork();                               // start process 0
    if (pid != 0) {
        wait(NULL);
        n = -5;
    }

    for (i = 1; i < n + 1; i++) {
        // odd/even level -- get number of children to start
        // NOTE: you may need to reverse this if
        if (i % 2 != 0)
            pcnt = 1;
        else
            pcnt = 2;

        // get parent pid
        ppid = getpid();

        // do the forks
        for (pcur = 0;  pcur < pcnt;  ++pcur)
            fork();

        // get current pid
        pid = getpid();

        // parent should wait on children
        if (pid == ppid) {
            while (wait(NULL) >= 0);
            break;
        }

        // print pid of leaves (not working correctly)
        if (i == n) {
            printf("Process: %d\n", pid);
        }
    }

    return 0;
}
0 голосов
/ 27 октября 2018

Исходя из вашего описания, ваша основная логика должна быть:

void fork_loop(int level, int stop) {
    if (level > stop) return;
    if (is_even(level)) {
        fork_child(level, stop);
        exit(0);
    } else {
        fork_child(level, stop);
        fork_child(level, stop);
        exit(0);
    }
}

Где fork_child() звонит fork().Дочерний процесс будет вызывать fork_loop(level+1, stop), а родительский будет возвращаться.

...