разветвление, проблемы ожидания в c - PullRequest
0 голосов
/ 09 мая 2011

По какой-то причине этот код немедленно выполняет родительские команды, завершая мои семафоры и нарушая управление потоком других программ.Может кто-нибудь сказать мне, почему waitpid () не работает?

    //Create child processes
pid = fork();
if(pid < 0){
    fprintf(stderr, "Fork Failed.\n");
    exit(1);
    return;
}else if(pid==0){
        if(execl("/home/tropix/hw11-2","/home/tropix/hw11-2",semarg,pipe_to_p3,pipe_to_p4,(char*)0)){
            fprintf(stderr, "File Exexecution of hw11-2 failed.\n");
            exit(1);
        }
} else {
    pid = fork();
    if(pid < 0){
        fprintf(stderr, "Fork Failed.\n");
        exit(1);
        return;
    } else if(pid==0){
        if(execl("/home/tropix/hw11-3","/home/tropix/hw11-3",shmarg,semarg,pipe_from_p2,pipe_to_p5_1, (char*)0)){
            fprintf(stderr, "File Execution of hw11-3 failed.\n");
            exit(1);
        }
    } else {
        pid = fork();
        if(pid < 0){
            fprintf(stderr, "Fork Failed.\n");
            exit(1);
            return;
        } else if (pid == 0){
            if(execl("/home/tropix/hw11-4","/home/tropix/hw11-4",shmarg,semarg,pipe_from_p2_2,pipe_to_p5_2, (char*)0)){
                fprintf(stderr, "File Execution of hw11-4 failed.\n");
                exit(1);
            }
        } else {
            pid = fork();
            if(pid < 0){
                fprintf(stderr, "Fork Failed.\n");
                exit(1);
                return;
            } else if (pid == 0){
                if(execl("/home/tropix/hw11-5","/home/tropix/hw11-5",semarg,pipe_from_p3,pipe_from_p4,(char*)0)){
                    fprintf(stderr, "File Execution of hw11-5 failed.\n");
                    exit(1);
                }
            } else if (pid > 0) {
            }
        }

    }

    //Closing Pipes
    close(pipe1[1]);
    close(pipe2[1]);
    close(pipe3[1]);
    close(pipe4[1]);
    close(pipe1[0]);
    close(pipe2[0]);
    close(pipe3[0]);
    close(pipe4[0]);

    //Wait for child process completetion
    waitpid(pid,NULL,0);
    printf("Child Processes Complete.\n");

    //Terminate Semaphores
    semctl(sem_id,0,IPC_RMID);

    //Terminate Shared Memory Segement
    shmctl(shmid, IPC_RMID, NULL);



}

}

Спасибо!

РЕДАКТИРОВАТЬ: ОК, я заменил waitpid на:

while (pid = waitpid(-1, NULL, 0)) {
       if (errno == ECHILD) {
          break;
       }
    }

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

Спасибо @ Джонатан

1 Ответ

6 голосов
/ 09 мая 2011

Вы ждете только одного процесса, а не всех.Это, наверное, одна проблема.Исправьте с помощью цикла на waitpid(), пока он не вернет «больше нет детей».

Структура кода оставляет желать лучшего - это кроличье гнездо вложенных if;ick!

Я беспокоюсь, что вы не закрываете достаточно каналов перед выполнением других команд.Вы можете быть в порядке, если команды не зависят от обнаружения EOF в канале;в противном случае вас ждет долгое ожидание.

Вам нужна функция типа:

#include <stdarg.h>

static void err_exit(const char *format, ...)
{
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    exit(EXIT_FAILURE);
}

Это упрощает обработку ошибок.Вы также можете сделать такие вещи, как автоматическое добавление PID, который умирает, или ошибку, которая вызвала выход, если хотите.

Мы также можем создать функцию для запуска другой команды:

static pid_t run_command(const char *cmd, const char *shmarg, const char *semarg,
                         const char *fdarg1, const char *fdarg2)
{
    pid_t pid = fork();
    if (pid < 0)
        err_exit("Failed to fork\n");
    else if (pid == 0)
    {
        execl(cmd, cmd, shmarg, semarg, fdarg1, fdarg2, (char *)0);
        err_exit("Failed to exec %s\n", cmd);
    }
    return pid;
}

Имея такие, мы можем уменьшить ваш код до этого ...

// Create child processes
pid_t pid1 = run_command("/home/tropix/hw11-2", semarg, pipe_to_p3,   pipe_to_p4);
pid_t pid2 = run_command("/home/tropix/hw11-3", shmarg, semarg, pipe_from_p2,   pipe_to_p5_1);
pid_t pid3 = run_command("/home/tropix/hw11-4", shmarg, semarg, pipe_from_p2_2, pipe_to_p5_2);
pid_t pid4 = run_command("/home/tropix/hw11-5", semarg, pipe_from_p3, pipe_from_p4);

Хммм ... некоторые из них имеют shmarg, а некоторые нет - это преднамеренное несоответствиеили случайно?Предположим, что мы намеренно, поэтому нам нужны две версии run_command ():

static pid_t run_cmd4(const char *cmd, const char *shmarg, const char *semarg,
                         const char *fdarg1, const char *fdarg2)
{
    pid_t pid = fork();
    if (pid < 0)
        err_exit("Failed to fork\n");
    else if (pid == 0)
    {
        execl(cmd, cmd, shmarg, semarg, fdarg1, fdarg2, (char *)0);
        err_exit("Failed to exec %s\n", cmd);
    }
    return pid;
}

static pid_t run_cmd3(const char *cmd, const char *semarg,
                         const char *fdarg1, const char *fdarg2)
{
    pid_t pid = fork();
    if (pid < 0)
        err_exit("Failed to fork\n");
    else if (pid == 0)
    {
        execl(cmd, cmd, semarg, fdarg1, fdarg2, (char *)0);
        err_exit("Failed to exec %s\n", cmd);
    }
    return pid;
}

А затем:

// Create child processes
pid_t pid1 = run_cmd3("/home/tropix/hw11-2", semarg, pipe_to_p3,   pipe_to_p4);
pid_t pid2 = run_cmd4("/home/tropix/hw11-3", shmarg, semarg, pipe_from_p2,   pipe_to_p5_1);
pid_t pid3 = run_cmd4("/home/tropix/hw11-4", shmarg, semarg, pipe_from_p2_2, pipe_to_p5_2);
pid_t pid4 = run_cmd3("/home/tropix/hw11-5", semarg, pipe_from_p3, pipe_from_p4);

Если это был мой код, имена переменныхбыло бы более равномерным - и, вероятно, в массивах:

// Create child processes
pid_t pid1 = run_cmd3("/home/tropix/hw11-2",         semarg, pipearg[0], pipearg[1]);
pid_t pid2 = run_cmd4("/home/tropix/hw11-3", shmarg, semarg, pipearg[2], pipearg[3]);
pid_t pid3 = run_cmd4("/home/tropix/hw11-4", shmarg, semarg, pipearg[4], pipearg[5]);
pid_t pid4 = run_cmd3("/home/tropix/hw11-5",         semarg, pipearg[6], pipearg[7]);

Тогда, наконец, у вас есть код:

// Closing Pipes
close(pipe1[1]);
close(pipe2[1]);
close(pipe3[1]);
close(pipe4[1]);
close(pipe1[0]);
close(pipe2[0]);
close(pipe3[0]);
close(pipe4[0]);

//Wait for child process completion
while (waitpid(0, NULL, 0) != 0)
    ;

printf("Child Processes Complete.\n");

// Remove Semaphores and Shared Memory
semctl(sem_id,0,IPC_RMID);
shmctl(shmid, IPC_RMID, NULL);

Я глубоко подозреваю, что run_cmdX() функционируетТакже необходимо закрыть большой выбор каналов - по крайней мере, каждый дескриптор каналов, не предназначенный для связи с их подпроцессом.

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

if (pipe(&pipes[0]) != 0 || pipe(&pipes[2]) != 0 ||
    pipe(&pipes[4]) != 0 || pipe(&pipes[6]) != 0)
    err_exit("Failed to create a pipe\n");

Затем я бы создал функцию:

void pipe_closer(int *pipes, int close_mask)
{
    for (i = 0; i < 8; i++)
    {
         if ((mask & (1 << i)) != 0)
             close(pipes[i]);
    }
}

Затем ее можно вызвать, чтобы закрыть ненужные каналы:

pipe_closer(pipes, 0xFF);   // Close them all - main()
pipe_closer(pipes, 0xFC);   // All except 0, 1
pipe_closer(pipes, 0xF3);   // All except 2, 3
pipe_closer(pipes, 0xCF);   // All except 4, 5
pipe_closer(pipes, 0x3F);   // All except 6, 7

Вы просто должны организовать передачу правильной маски с каждой из функций run_cmdN() и правильные вызовы.Если массив pipes не является глобальным, его тоже нужно будет передать.Я бы также посмотрел, как аккуратно кодировать данные, чтобы вызовы run_cmdN() были как можно более регулярными и симметричными.


Kernighan & Plauger's * Элементы стиля программирования"(2nd Edn, 1978; трудно найти, я подозреваю) содержит много великолепных цитат.Непосредственно подходящим является (выделение жирным шрифтом, курсив в оригинале):

  • [T] вызов подпрограммы позволяет нам суммировать неровностей всписок аргументов , где мы можем быстро увидеть, что происходит.
  • Сама подпрограмма суммирует закономерности кода , поэтому повторяющиеся шаблоны не нужно использовать.

Это можно рассматривать как часть принципа программирования СУХОЙ (не повторяйся).Вызов функции err_exit() заключает в себе три или четыре строки кода - печать и выход плюс фигурные скобки, в зависимости от вашего предпочтительного расположения.Функции run_command() являются основным случаем DRY.Предложенный pipe_closer() является еще одним.

...