Форкинг и управление процессами

Информация о системе: я использую 64-битную Ubuntu 10.10 на ноутбуке двухмесячной давности.

Привет всем, у меня есть вопрос о функции fork() в C. Из ресурсов, которые я использую (Stevens/Rago, YoLinux и Opengroup), я понимаю, что когда вы разветвляете процесс, как родитель, так и дочерний продолжить выполнение со следующей команды. Поскольку fork() возвращает 0 дочернему элементу, а идентификатор процесса дочернего элемента - родительскому, вы можете изменить их поведение с помощью двух операторов if, одного if(pid = 0) для дочернего элемента и if(pid > 0), предполагая вы разветвились с помощью pid = fork().

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

Целью моей программы является создание «дерева процессов», в котором каждый процесс имеет двух дочерних элементов, вплоть до глубины 3, таким образом создавая 14 дочерних элементов исходного исполняемого файла. Каждый процесс печатает идентификатор своего родительского процесса и свой идентификатор процесса до и после разветвления.

Мой код выглядит следующим образом и правильно прокомментирован, аргументы командной строки должны быть «ofile 3 2 -p» (я еще не реализовал флаги -p/-c):

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


int main (int argc, char *argv[])
{
    if(argc != 5)//checks for correct amount of arguments
    {
        return 0;
    }

    FILE * ofile;//file to write to
    pid_t pid = 1;//holds child process id
    int depth = atoi(argv[2]);//depth of the process tree
    int arity = atoi(argv[3]);//number of children each process should have

    printf("%d%d", depth, arity);

    ofile = fopen(argv[1], "w+");//opens specified file for writing



    int a = 0;//counter for arity
    int d = 0;//counter for depth
    while(a < arity && d < depth)//makes sure depth and arity are within limits, if the children reach too high(low?) of a depth, loop fails to execute
                                  //and if the process has forked arity times, then the loop fails to execute
    {
        fprintf(ofile, "before fork: parent's pid: %d, current pid: %d\n", getppid(), getpid());//prints parent and self id to buffer
        pid = fork(); //forks program
        if(pid == 0)//executes for child
        {
            fprintf(ofile, "after fork (child):parent's pid: %d, current pid: %d\n", getppid(), getpid());//prints parent's id and self id to buffer
                    a=-1;//resets arity to 0 (after current iteration of loop is finished), so new process makes correct number of children
            d++;//increases depth counter for child and all of its children
        }
        if(pid > 0)//executes for parent process
        {
        waitpid(pid, NULL, 0);//waits on child to execute to print status
        fprintf(ofile, "after fork (parent):parent's pid: %d, current pid: %d\n", getppid(), getpid());//prints parent's id and self id to buffer
        }
        a++;//increments arity counter
    }


    fclose(ofile);
}

Когда я запускаю «gcc main.c -o ptree», а затем «ptree ofile 3 2 -p», консоль несколько раз спамит «32», а файл «ofile» имеет, казалось бы, правильный формат, но немного слишком большой для того, что, как я думаю, должна делать моя программа, показывая 34 дочерних процесса, когда должно быть 2 ^ 3 + 2 ^ 2 + 2 ^ 1 = 14. Я думаю, что это как-то связано с утверждением, которое печатает «32», так как это, возможно, порождает больше вилок, чем предполагалось.

Любая помощь будет принята с благодарностью.


person joedillian    schedule 16.12.2010    source источник
comment
Если это не обман stackoverflow.com/questions/4459685/functionality-of-fork, нужно уточнить, почему это не так.   -  person unwind    schedule 16.12.2010
comment
Что ж, ответы были сосредоточены на некоторых ошибках, которые я сделал, которые, по-видимому, не связаны с моим центральным вопросом, почему этот оператор печати для этих 32-х выполняется несколько раз, когда они находятся перед моими разветвлениями и циклами. Все ответы там были сосредоточены на моей ошибке, связанной с тем, что я не использовал atoi() и имел запутанный вывод файла, ни один из которых не отвечает на этот вопрос. Я решил опубликовать изменения, которые люди предложили мне внести, в надежде не отвлекать потенциальных ответчиков от моего основного вопроса снова.   -  person joedillian    schedule 16.12.2010
comment
Я бы хотел, чтобы код был правильно прокомментирован, когда он содержит такие комментарии, как a++;//увеличивает счетчик арности и pid = fork(); // разветвляет программу!   -  person Graham Borland    schedule 16.12.2010
comment
Я новичок в C graham, мне нравится следить за тем, чтобы почти все было прокомментировано, чтобы, когда я читаю свой собственный код, я точно знал, что такое оператор и на что ссылается, даже не глядя на фактический код. Я уверен, что ваши отраслевые стандарты и тому подобное очень полезны для сохранения порядка в коде среди профессиональных программистов, но дайте нам, студентам, немного поблажки, а?   -  person joedillian    schedule 16.12.2010


Ответы (1)


Когда вы вызываете printf, данные сохраняются во внутреннем буфере. Когда вы разветвляете, этот буфер наследуется дочерним элементом. В какой-то момент (когда вы снова вызываете printf или когда вы закрываете файл) буфер сбрасывается и данные записываются в нижележащий файловый дескриптор. Чтобы предотвратить наследование данных в буфере потомком, вы можете очистить ФАЙЛ * перед вызовом fork через fflush.

person William Pursell    schedule 16.12.2010
comment
Вау, спасибо большое. Я даже не могу найти упоминания об этой ситуации в моем учебнике, хотя она вполне может быть там, и мой учитель никогда не упоминал об этом. Я уверен, что никогда бы не догадался об этом сам. Ваше здоровье! - person joedillian; 16.12.2010
comment
Также стоит отметить, что буфер автоматически дублируется при разветвлении, поэтому вы получаете дублированный вывод. - person Douglas Leeder; 16.12.2010
comment
Да, я думаю, что Уильям намекал на то, что буфер унаследован ребенком. Спасибо за ваш вклад, хотя Дуглас. - person joedillian; 16.12.2010
comment
Также стоит отметить, что stdout по умолчанию буферизуется строкой, а это означает, что если вы всегда заканчиваете свои форматы printf() \n, вы не увидите проблемы. Поскольку это довольно распространено, возможно, поэтому учебник / учитель не упомянул эту проблему. - person caf; 17.12.2010
comment
Не совсем корректно говорить, что stdout по умолчанию буферизуется строкой. Если стандартный вывод связан с tty, то он по умолчанию буферизуется строкой. Но в большинстве случаев стандартный вывод связан с каналом, и в этом случае он по умолчанию буферизуется блоками. Он также буферизуется блоками по умолчанию, когда связан с обычным файлом. - person William Pursell; 21.09.2017