Стратегия спасения при работе с трубами, вилкой - PullRequest
1 голос
/ 29 ноября 2011

Я написал небольшую программу, использующую fork для создания новых процессов, которые используют pipe s для связи. Применяя защитное программирование, я проверяю каждое возвращаемое значение. Если возвращаемое значение указывает, что что-то пошло не так, я хочу освободить все ресурсы, закрыть все каналы и родительский процесс, чтобы дождаться завершения его дочерних процессов. Теперь, каков наилучший способ помочь в случае возникновения ошибки?

На данный момент я делаю это так:

    /* initialize pipes */
if(pipe(p1fd) == -1) {
    (void) printError("Could not init pipe 1");
    exit(EXIT_FAILURE);
}
if(pipe(p2fd) == -1) {
    (void) printError("Could not init pipe 2");
    (void) close(p1fd[0]);
    (void) close(p1fd[1]); 
    exit(EXIT_FAILURE);
}

switch (pid = fork()) {
    case -1: 
        (void) printError("Could not fork");
        (void) close(p1fd[0]);
        (void) close(p1fd[1]);
        (void) close(p2fd[0]);
        (void) close(p2fd[1]);  
        exit(EXIT_FAILURE);
        break;
    case 0: /* child process */
        break;
    default: /* parent process */       
        break;
}   

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

Ответы [ 2 ]

4 голосов
/ 29 ноября 2011

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

if (pipe(p1fd) == -1) {
    printError("Could not init pipe 1");
    goto pipe1_fail;
}

if (pipe(p2fd) == -1) {
    printError("Could not init pipe 2");
    goto pipe2_fail;
}

switch (pid = fork()) {
    case -1: 
        printError("Could not fork");
        goto fork_fail;

    case 0: /* child process */
        ...
        exit(...);

    default: /* parent process */       
        ...
        exit(...);
}

fork_fail:
close(p2fd[0]);
close(p2fd[1]);

pipe2_fail:
close(p1fd[0]);
close(p1fd[1]);

pipe1_fail:
exit(EXIT_FAILURE);
2 голосов
/ 29 ноября 2011

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

Пример здесь:

/* initialize pipes */
if(pipe(p1fd) == -1) {
    (void) printError("Could not init pipe 1");
    goto error;
}

if(pipe(p2fd) == -1) {
    (void) printError("Could not init pipe 2");
    goto closep1fd;
}

switch (pid = fork()) {
    case -1: 
        (void) printError("Could not fork");
        goto closep2p1fd;
    case 0: /* child process */
        break;
    default: /* parent process */       
        break;
}   

closep2p1fd:
    close(p2fd[0]);
    close(p2fd[1]); 
closep1fd:
    close(p1fd[0]);
    close(p1fd[1]); 
error:
    exit(EXIT_FAILURE);
    return -1; // maybe better if it is a deeply nested function
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...