fork() заставляет печатать заголовки столбцов для каждого процесса

Я пишу простую программу на C, используя fork() для создания двоичного дерева процессов. Я могу получить весь вывод, который мне нужен (pid процесса, его родителя и двух его дочерних элементов). К сожалению, каждый разветвленный процесс хочет распечатать заголовки столбцов. Как мне убедиться, что printf для заголовков выполняется только один раз?

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

int main(int argc, char *argv[]){

//Declarations
int i;
int child_1_pid, child
Level Procs   Parent  Child1  Child2
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
0 30796   24743   30797   30798
1 30797   30796   30799   30800
1 30798   30796   30801   30802
pid; int num_levels = atoi(argv[1]); //Output banners //execlp("/bin/echo", "echo", "Level\tProcs\tParent\tChild1\tChild2\nNo.\tID\tID\tID\tID", (char *) NULL); //if(getpid() > 0) printf("Level\tProcs\tParent\tChild1\tChild2\nNo.\tID\tID\tID\tID"); //Creates binary tree of processes for(i = 0; i < num_levels; i++){ if((child_1_pid = fork()) && (child
Level Procs   Parent  Child1  Child2
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
0 30796   24743   30797   30798
1 30797   30796   30799   30800
1 30798   30796   30801   30802
pid = fork())){ printf("\n%d\t%d\t%d\t%d\t%d", i, getpid(), getppid(), child_1_pid, child
Level Procs   Parent  Child1  Child2
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
0 30796   24743   30797   30798
1 30797   30796   30799   30800
1 30798   30796   30801   30802
pid); sleep(2); //prevents parent from terminating before child can get ppid (parent's pid) break; //why? } }//end for printf("\n"); //EXPLAIN ME!! exit(0); }//end main

Там есть еще немного кода (на самом деле проверка ошибок), но моя настоящая проблема заключается в том, что printf в разделе выходных баннеров выполняется несколько раз, выдавая такой вывод (но правильно выровненный):

Level Procs   Parent  Child1  Child2
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
0 30796   24743   30797   30798
1 30797   30796   30799   30800
1 30798   30796   30801   30802

Я попробовал несколько идей (в том числе закомментированных в разделе баннеров), но ничего не работает, а большинство «исправлений» еще больше усугубляют проблему!


person Richard Martinez    schedule 22.02.2011    source источник
comment
Думаю, я могу объяснить странную печать, которую вы видите. Ваши sleep(2) и printf("\n") могут быть fflush(0).   -  person nmichaels    schedule 22.02.2011
comment
Исправит ли что-нибудь добавление fflush(stdout); после printf? Возможно, буферы stdio копируются в дочерние процессы и печатаются только там.   -  person Jeremiah Willcock    schedule 22.02.2011
comment
Да! Добавление fflush устраняет проблему множественной печати. Я также удалил печать \n внизу, чтобы избежать лишних пустых строк. Кроме того, как отмечено ниже, мне пришлось поставить fflush после строки печати процесса (внутри цикла for).   -  person Richard Martinez    schedule 22.02.2011


Ответы (4)


Во-первых, if в цикле for ведет себя не так, как вам хотелось бы. Помните, что после форка он возвращает дочерний PID в родительском процессе и 0 в дочернем. Таким образом, внутри цикла первая вилка присваивает значение childifpid в родительском элементе и переходит ко второму предложению. Дочерний элемент не входит в if, а переходит к следующей итерации цикла for. То же самое происходит и со вторым пунктом. Таким образом, только основной процесс должен когда-либо иметь возможность войти в тело if, но не дочерний процесс. Интересно, почему вывод говорит об обратном.

Итак, чтобы получить свое «бинарное дерево», у вас должно быть это:

// COMPLETELY UNTESTED
for(i = 0; i < num_levels; i++){
    if (!(child_1_pid = fork()) || !(child_2_pid = fork())) {
        printf("\n%d\t%d\t%d\t%d\t%d", i, getpid(), getppid(), child_1_pid, child_2_pid);
        // A child process, go on to next iteration.
        continue;
    }

    // A parent process. Wait for children, then stop.
    if (child_1_pid) wait();
    if (child_2_pid) wait();
    break;
}

Странный вывод баннеров связан со сбросом потоков. Обычно fprintf сбрасывается только на новой строке (\n), IIRC. Таким образом, в буфере после разветвления все еще есть вещи, которые еще не были сброшены, и каждый потомок запускает printf("\n"); и, таким образом, сбрасывает содержимое буфера.

Решение состоит в том, чтобы либо добавить "\n" в конец самого первого printf, либо вызвать fflush(stdout); перед циклом for.

person DarkDust    schedule 22.02.2011
comment
Вы совершенно правы в необходимости fflush перед циклом for. Мне также пришлось добавить один после оператора печати баннера. Спасибо за совет о printf и очистке новых строк .... это прояснило ситуацию. - person Richard Martinez; 22.02.2011
comment
это создаст несбалансированное двоичное дерево, поскольку только родительский процесс из первого вызова fork() будет fork() во второй раз из-за короткого замыкания. Это может быть или не быть вашим намерением. - person SiegeX; 23.02.2011
comment
@SiegeX: Ты прав. Использование | вместо || должно исправить это. - person DarkDust; 23.02.2011

Вот кое-что, чтобы попробовать, хотя я немного заржавел с этим материалом. В строке, где вы распечатываете свои баннеры:

printf("Уровень\tProcs\tParent\tChild1\tChild2\nNo.\tID\tID\tID\tID");

Может случиться так, что все после \n останется в выходном буфере, поэтому оно все еще там, когда каждый дочерний элемент разветвляется. Попробуйте добавить еще один \n в конце этого printf и удалить \n из начала printf внутри цикла.

person Will    schedule 22.02.2011
comment
Я думаю, это правильная идея. В приведенном выше ответе упоминалось, что printf сбрасывается на \n, так что вы, похоже, правы. - person Richard Martinez; 22.02.2011
comment
@Джон Смит Да. Однако у @DarkDust определенно был более подробный ответ, чем у меня. - person Will; 22.02.2011

Заменять:

printf("Level\tProcs\tParent\tChild1\tChild2\nNo.\tID\tID\tID\tID");

С участием:

puts("Level\tProcs\tParent\tChild1\tChild2\nNo.\tID\tID\tID\tID");

Заменять:

printf("\n%d\t%d\t%d\t%d\t%d", i, getpid(), getppid(), child_1_pid, child_2_pid);

С участием:

printf("%d\t%d\t%d\t%d\t%d\n", i, getpid(), getppid(), child_1_pid, child_2_pid);

Удалять:

printf("\n");
person SiegeX    schedule 22.02.2011

Читайте 2.5.1 здесь:

http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html

Обратите внимание, что после fork() существуют два дескриптора там, где раньше был один. Приложение должно гарантировать, что, если оба дескриптора когда-либо будут доступны, они оба будут в состоянии, когда другой может стать активным дескриптором первым. Приложение должно подготовиться к fork() точно так же, как если бы это была смена активного дескриптора. (Если единственным действием, выполняемым одним из процессов, является одна из exec-функций или _exit() (не exit()), доступ к дескриптору в этом процессе никогда не осуществляется.)

Это означает, что перед вызовом fork вы должны вызывать fflush для любых потоков, которые вы собираетесь использовать в обоих процессах после fork.

person R.. GitHub STOP HELPING ICE    schedule 22.02.2011