Обработка ошибок от execvp()

Я немного не понимаю, как обрабатывать ошибки из execvp(). Мой код пока выглядит так:

int pid = fork();
if (pid < 0) {
    // handle error.
}
else if (pid == 0) {
    int status = execvp(myCommand,myArgumentVector); // status should be -1 because execvp
                                                     // only returns when an error occurs
    // We only reach this point as a result of failure from execvp
    exit(/* What goes here? */);
}
else {
    int status;
    int waitedForPid = waitpid(pid,&status,0);
    //...
}

Есть три случая, которые я пытаюсь решить:

  1. myCommand,myArgumentVector действительны, и команда выполняется правильно.
  2. myCommand,myArgumentVector являются допустимыми параметрами, но что-то пошло не так при выполнении myCommand.
  3. myCommand,myArgumentVector являются недопустимыми параметрами (например, myCommand не может быть найден) и вызов execvp() завершается ошибкой.

Моя главная забота заключается в том, чтобы родительский процесс имел всю информацию, необходимую для правильной обработки дочерней ошибки, и я не совсем уверен, как это сделать.

В первом случае программа предположительно закончилась со статусом выхода 0. Это означает, что если я вызову WIFEXITED(status) в макросе, я должен получить true. Я думаю, что это должно работать нормально.

Во втором случае программа предположительно завершилась со статусом выхода, отличным от 0. Это означает, что если бы я вызывал WEXITSTATUS(status), я должен был бы получить конкретный статус выхода дочернего вызова myCommand (пожалуйста, сообщите, если это неверно).

Третий случай вызывает у меня много путаницы. Таким образом, если execvp() терпит неудачу, то ошибка сохраняется в глобальной переменной errno. Но эта глобальная переменная доступна только из дочернего процесса; родитель как совершенно отдельный процесс, я не думаю, что может это увидеть. Значит ли это, что я должен звонить exit(errno)? Или я должен делать что-то еще здесь? Кроме того, если я вызову exit(errno), как я могу получить значение errno обратно из status в родительском элементе?

Мое понимание все еще немного слабое, поэтому я ищу либо подтверждение, либо исправление в моем понимании того, как обращаться с этими тремя случаями.


person Kvass    schedule 19.11.2013    source источник
comment
-1 или EXIT_FAILURE должно быть достаточно.   -  person Mihai Maruseac    schedule 19.11.2013
comment
Но я хочу сохранить значение errno...   -  person Kvass    schedule 19.11.2013
comment
Тогда почему бы не выйти (-errno)? Возможно, нужно разыграть его, но что-то в этой форме может сработать...   -  person Michael    schedule 19.11.2013
comment
Как это помогает? Я не понимаю взаимосвязь между значением кода выхода и целочисленным статусом, назначаемым waitpid.   -  person Kvass    schedule 19.11.2013


Ответы (4)


Вот простой код, который я пробовал.

if(fork() == 0){
   //do child stuff here
   execvp(cmd,arguments); /*since you want to return errno to parent
                            do a simple exit call with the errno*/
   exit(errno);
}
else{                
    //parent stuff
    int status;
    wait(&status);       /*you made a exit call in child you 
                           need to wait on exit status of child*/
    if(WIFEXITED(status))
         printf("child exited with = %d\n",WEXITSTATUS(status));
                              //you should see the errno here
}
person Raju Kunde    schedule 19.11.2013
comment
Это было проверено в случае, когда cmd недействителен и приводит к сбою execvp? И значит ли это, что если я exit(errno), то я могу получить доступ к значению errno с помощью WEXITSTATUS(status)? - person Kvass; 19.11.2013
comment
Макрос WIFEXITED возвращает ненулевое значение, если дочерний процесс завершился нормально с помощью exit или _exit. - person Raju Kunde; 19.11.2013
comment
Спасибо, но не то, что я спросил. - person Kvass; 19.11.2013
comment
Хорошо. Если WIFEXITED истинно для состояния, макрос WEXITED(status) возвращает младшие 8 бит значения состояния exit из дочернего процесса. - person Raju Kunde; 19.11.2013
comment
это где errno будет храниться? Если дочерний элемент запускает exit(errno), каким именно будет значение статуса, разбитое на его различные битовые компоненты? - person Kvass; 19.11.2013
comment
Пройдите через это - person Raju Kunde; 19.11.2013
comment
По ссылке, предоставленной @RajuKunde, я до сих пор не могу понять, как вы могли бы получить errno, если бы это было число больше 8 бит (›= 256) - person Pro Q; 24.11.2018
comment
На самом деле биты статуса выхода обычно представляют собой старшие 8 бит 16-битного значения выхода (начиная с эпохи, когда 16-битный int был нормальным — это было давно назад). Вы можете использовать printf("0x%.4X\n", status);, чтобы увидеть числа. Младшие 8 бит фиксируют номер сигнала (обычно). - person Jonathan Leffler; 19.08.2019
comment
См. ExitCodes больше 255 — возможно? — а также внимательно посмотрите на sigaction() и обсуждение sigaction() и Сигнальные действия. - person Jonathan Leffler; 19.08.2019

В случае 1 execvp() не возвращается. Статус, возвращаемый родительскому процессу, будет статусом выхода дочернего процесса — то, что он отправляет в exit() или что он возвращает из main(), или может быть, что дочерний процесс умирает от сигнала, и в этом случае статус выхода отличается, но обнаруживается так. (WIFSIGNALED и т. д.). Обратите внимание, что это означает, что статус не обязательно должен быть равен нулю.

Не совсем понятно (мне), что вы имеете в виду в случае 2. Если команда запускается, но отклоняет параметры, с которыми она вызывается, на самом деле это случай 1, но вероятность того, что статус выхода будет равен нулю, должна быть небольшой. (хотя известно, что программы завершают работу со статусом 0 при ошибке). В качестве альтернативы команда не может быть найдена или найдена, но не является исполняемой, и в этом случае возвращается execvp(), и у вас есть случай 3.

В случае 3 вызов execvp() завершается ошибкой. Вы знаете это, потому что оно возвращается; успешный execvp() никогда не возвращается. Нет смысла тестировать возвращаемое значение execvp(); сам факт того, что он возвращается, означает, что он потерпел неудачу. Вы можете сказать, почему это не удалось, по настройке errno. POSIX использует статусы выхода 126 и 127 — см. xargs и system(). Вы можете просмотреть коды ошибок на странице execvp(), чтобы определить, когда следует вернуться. либо из тех, либо какое-то другое ненулевое значение.

person Jonathan Leffler    schedule 19.11.2013
comment
Примером случая 2 может быть что-то вроде команды cd foobar, где foobar не является допустимым каталогом. Exec находит команду cd и выполняет ее (аргументы действительны), но затем возникает ошибка при выполнении программы cd. Примером случая 3 может быть что-то вроде foobar, где foobar просто недопустимая программа, и поэтому сам вызов execvp завершается ошибкой. - person Kvass; 19.11.2013
comment
По сути, то, что мне интересно с № 3, это то, что в случае сбоя execvp, как я могу сообщить родителю, почему? Должен ли я выйти с errno от ребенка? - person Kvass; 19.11.2013
comment
Хммм... ну, в той мере, в какой вы можете найти команду cd (это встроенная оболочка по очень веским причинам, но в Mac OS X есть /usr/bin/cd), ваш пример случая 2 на самом деле является случаем 1: команда была выполнена, но выполнение не удалось. execvp() не возвращается; выполненная команда завершается с некоторым (ненулевым) статусом. Случай 3 легко понять; вы можете определить его, потому что execvp() может вернуть ENOENT. В случае 3 вы выходите с соответствующим ненулевым статусом выхода. Большая часть моего кода печатает сообщение об ошибке, но завершается со статусом 1, общим статусом «что-то пошло не так». - person Jonathan Leffler; 19.11.2013

В третьем случае errno также доступен из родителя, поэтому вы можете просто выйти (errno). Однако это не лучший выход, так как значение errno может измениться к моменту выхода.

Чтобы быть более уверенным, что вы не потеряете errno, если у вас есть код между вашими вызовами exec() и exit(), присвойте errno int:

execvp(<args>);

int errcode=errno;

/* other code */

exit(errcode);

Что касается вашего другого вопроса, статус выхода нельзя напрямую сравнить с errno, и вы в любом случае не должны пытаться получить errno из чего-либо, кроме errno (как указано выше).

Эта документация может помочь: http://www.gnu.org/software/libc/manual/html_node/Exit-Status.html

person achan    schedule 19.11.2013
comment
Итак, скажем, я делаю exit(errcode) со значением errno, полученным в результате выполнения execvp. Теперь в родительском waitpid(pid,&status,0) переменной status присвоено какое-то значение. Это значение эквивалентно errcode? Или, если нет, то каким образом это связано? - person Kvass; 19.11.2013
comment
Также, когда вы говорите, что errno доступен из родителя, вы имеете в виду только выполнение exit(errcode), верно? Или глобальные переменные являются общими для процессов...? - person Kvass; 19.11.2013
comment
Они не связаны с системным вызовом bash. Вызов exec может иметь несколько ошибок, при этом errno может меняться много раз, а окончательный errno не обязательно должен относиться к статусу выхода. Это видно из диапазона возвращаемых значений — 8 бит для статуса и модифицируемое lvalue типа int для errno. Когда execvp завершается (предположительно с помощью функции exit() или эквивалентной, она не возвращает выход (errno)). Так что статус не соответствует errno. - person achan; 19.11.2013

Вот мой код: он отлично работает. Дочерний статус возвращается в waitpid, поэтому он сообщает, что дочерний процесс выполнен успешно или нет. //объявление переменной идентификатора процесса pid_t pid, ret_pid;

//fork a child process is assigned 
//to the process id
pid=fork();

DFG_STATUS("Forking and executing process in dfgrunufesimulator %d \n", pid);

//code to show that the fork failed
//if the process id is less than 0
if(pid<0)
{
    return DFG_FAILURE;
}
else if(pid==0)
{
    //this statement creates a specified child process
    exec_ret = execvp(res[0],res);  //child process

DFG_STATUS("Process failed ALERT exec_ret = %d\n", exec_ret);
exit(errno);

}
//code that exits only once a child 
//process has been completed
else
{
  ret_pid = waitpid(pid, &status, 0);
  if ( ret_pid == -1) 
  { 
    perror("waitpid"); 
    return DFG_FAILURE; 
  }

   DFG_STATUS("ret_pid = %d, pid = %d, child status = %d\n",ret_pid,pid,status);


  if (WIFEXITED(status)) {
        DFG_STATUS("child exited, status=%d\n", WEXITSTATUS(status));


    } else if (WIFSIGNALED(status)) {
        DFG_STATUS("child killed (signal %d)\n", WTERMSIG(status));


    } else if (WIFSTOPPED(status)) {
        DFG_STATUS("child stopped (signal %d)\n", WSTOPSIG(status));


#ifdef WIFCONTINUED     /* Not all implementations support this */
    } else if (WIFCONTINUED(status)) {
        DFG_STATUS("child continued\n");
#endif
    } else {    /* Non-standard case -- may never happen */
        DFG_STATUS("Unexpected status (0x%x)\n", status);
    }

  if(status != 0) /* Child process failed to execute */
    return DFG_FAILURE;

  result = DFG_SUCCESS;
}
person usman chaudhry    schedule 11.05.2017