fork () заставляет заголовки столбцов печатать для каждого процесса - PullRequest
1 голос
/ 22 февраля 2011

Я пишу простую 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_2_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_2_pid = fork())){
        printf("\n%d\t%d\t%d\t%d\t%d", i, getpid(), getppid(), child_1_pid, child_2_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

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

Ответы [ 4 ]

3 голосов
/ 22 февраля 2011

Во-первых, if в цикле for ведет себя не так, как вы этого хотите. Помните, что после разветвления он возвращает дочерний PID в родительском процессе и 0 в дочернем. Таким образом, внутри цикла первая ветвь присваивает значение child_1_pid в родительском элементе и переходит ко второму предложению. Дочерний элемент не вводит 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.

2 голосов
/ 22 февраля 2011

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

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

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

1 голос
/ 22 февраля 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");
0 голосов
/ 22 февраля 2011

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

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

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

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

...