execv и fork: сообщить родителю, что дочернему элементу не удалось выполнить файл

Как главный процесс может узнать, что дочернему процессу не удалось выполнить файл (например, нет такого файла или каталога)? Например, как в следующем коде заставить run() возвращать что-то отличное от 0? Спасибо!

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

enum ErrorType { noError, immediateFailure, returnFailure, forkFailure };

using namespace std;

int run(void)
{
    int error = noError;
    //char proc[] = "/bin/uname";
    char proc[] = "/usr/bin/Idontexist";
    char *const params[] = {proc, NULL};

    pid_t pid = fork();

    printf("pid = %d\n",pid);

    if (pid == 0)
    {
        if ( execv(proc,params)==-1 )
        {
            error = immediateFailure;
        }
        printf("child: error = %d\n",error);
    }
    else if (pid > 0)
    {
        /*  This is the parent process
         *  incase you want to do something
         *  like wait for the child process to finish
         */
        int waitError;
        waitpid(pid, &waitError, 0);

        if ( waitError )
            error = returnFailure;

        printf("parent: error = %d, waitError = %d\n",error,waitError);
    }
    else
    {
        error = forkFailure;
    }

    return error;
}

int main(void)
{
    printf("run() = %d\n",run());

    return 0;
}

выход:

pid = 4286
pid = 0
child: error = 1
run() = 1
parent: error = 0, waitError = 0
run() = 0

person psilouette    schedule 13.04.2016    source источник


Ответы (4)


Вам нужно вернуть код ошибки из main или exit() со значением. Теперь вы returnвыполняете 0 из main во всех случаях, и это будет код выхода дочернего элемента, который родитель получает от waitpid.

Лучшее решение — добавить _exit(error) в дочерний процесс:

printf("child: error = %d\n",error);
_exit(error);

В любом случае используйте целочисленные константы вместо случайных констант перечисления enum. 1 и 2 являются общими возвращаемыми значениями многих команд unix, где 1 означает, что команда не была успешной (например, grep не нашло совпадений), а 2+ означает, что команда действительно не удалась (неправильные аргументы или что-то подобное)`.

bash, zsh и другие оболочки используют 127 в качестве кода выхода, чтобы сообщить, что команда не найдена/не может быть запущена; таким образом, это было бы рекомендовано:

#define COMMAND_NOT_RUNNABLE 127

/* ... */

execv(proc, params);  // exec never returns if successful.
perror(proc);
_exit(COMMAND_NOT_RUNNABLE);

Причина использования _exit в дочернем процессе заключается в том, что он не очищает буферы stdio (которые также существуют на родительской стороне) и не запускает перехватчики atexit. (Спасибо Эндрю Хенле)

person Antti Haapala    schedule 13.04.2016
comment
Немного устарело, но вызов exit() после неудачного exec() после fork() может вызвать серьезные проблемы. exit() сбрасывает все выходные FILE * потоки и содержащиеся в них данные, унаследованные от адресного пространства родительского процесса. Если родительский процесс является многопоточным, он может заблокироваться, если какой-либо из потоков был заблокирован другими потоками при вызове exec(). Также будут вызываться функции, зарегистрированные atexit(). Если какой-либо код C++ линкуется, будут вызываться статические деструкторы. _exit() гораздо безопаснее. - person Andrew Henle; 10.10.2019

В моем случае exit(EXIT_FAILURE) не приводит к возврату в waitpid() EXIT_FAILURE статус дочернего процесса, всегда 0. Мое решение состоит в том, чтобы abort() дочерний процесс, если execv()/execve() не удалось выполнить.

person nnnsoft    schedule 10.10.2019

Проблема заключается в обработке ошибки после сбоя execv. Дочерний элемент устанавливает error в immediateFailure и возвращает его в main. Затем main печатает номер ошибки и возвращает 0. Но 0 означает успех, поэтому родительский процесс считает, что дочерний процесс преуспел.

Решение состоит в том, чтобы вернуть номер ошибки из main или вызвать exit в дочернем коде, например.

if (pid == 0)
{
    execv(proc,params);
    error = immediateFailure;
    printf("child: error = %d\n",error);
    exit( error );
}

Обратите внимание, что вам не нужно проверять возвращаемое значение из execv, так как execv не вернется в случае успеха. Он вернется только в случае сбоя и всегда будет возвращать -1.

person user3386109    schedule 13.04.2016

В дополнение к предложенным exit(error); следует указать конкретные варианты waitpid. Ваш код у меня не работал, пока я не изменил вызов на waitpid(pid, &waitError, WUNTRACED | WCONTINUED);

person GMichael    schedule 13.04.2016