Не знаете, как узнать, завершается ли дочерний процесс завершением или сигналом - PullRequest
0 голосов
/ 19 октября 2018

Я пытаюсь написать программу, в которой

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

Следовательно, я попытался сделать то же самое, я добавил обработчик SIGINT для моего родительского процесса, и я предполагаю, что когда вызывается функция execvp (),дочерний процесс будет выполнять процесс SIGINT с использованием обработчиков по умолчанию.

 void sigint_handler(int signum){
    printf("");
 }

int main(int argc, char **argv){

pid_t pid = fork();

if(pid<0)
{
printf("fork: error");
exit(-1);

}
else if(pid>0){
    int status;
    signal(SIGINT,sigint_handler);
    pid_t child_pid= waitpid(pid, &status, 0);
    printf("");
}
else if(pid==0){
    printf("");
    if(execvp(argv[1],&argv[1]) == -1) {
    printf(" ");
    exit(-1);
      }

}

}

Достаточно ли этого кода для обработки того же самого?Кроме того, если мой дочерний процесс завершается с помощью SIGINT или SIGSEGV и т. Д., Как мне узнать, был ли мой дочерний процесс завершен после завершения или из-за сигнала и какой сигнал использовался.

1 Ответ

0 голосов
/ 19 октября 2018

Значение из wait() или друзей для статуса говорит вам - используйте макросы WIFEXITED() и WIFEXITSTATUS() для обычного выхода или WIFSIGNALED() и WIFTERMSIG() для сигнала (макросы IF проверяют, как этовыход, конечно).Есть также WIFSTOPPED(), WIFCONTINUED() и WSTOPSIG() для управления заданиями.

См. POSIX waitpid() (какие документы wait() тоже).И большинство систем на основе Unix также предоставляют WCOREDUMP(), но POSIX не требует этого.Вы также можете посмотреть статус выхода в шестнадцатеричном формате (4 цифры), и это не займет много времени, чтобы определить шаблоны, но вы должны использовать макросы.


Для первой части моеговопрос, будет ли мой вышеупомянутый код игнорировать сигнал SIGINT, пока он еще находится в родительской части процесса?

Осторожно: избегать использования printf () в обработчиках сигналов .

Непонятно, как работает ваш код обработки сигналов;Вы бы нарезали слишком много кода из вопроса.Однако вы уже добавили достаточно отсутствующего кода, и, как я уже догадался, ваш код не является пуленепробиваемым, поскольку показанный вызов signal() идет после fork() и в родительском процессе.Вы должны отключить сигнал перед вызовом fork();тогда ребенок должен снова включить его.Это обеспечивает правильную защиту процессов.

Например (файл sig11.c - скомпилирован для создания sig11 с использованием gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes sig11.c -o sig11):

#include <assert.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    if (argc < 2)
    {
        fprintf(stderr, "Usage: %s command [arg ...]\n", argv[0]);
        return 1;
    }
    bool handle_signals = (signal(SIGINT, SIG_IGN) != SIG_IGN);

    pid_t pid = fork();

    if (pid < 0)
    {
        fprintf(stderr, "fork: error\n");
        return 1;
    }
    else if (pid > 0)
    {
        int status;
        pid_t child_pid = waitpid(pid, &status, 0);
        printf("child %d exited with status 0x%.4X\n", (int)child_pid, status);
    }
    else
    {
        assert(pid == 0);
        if (handle_signals)
            signal(SIGINT, SIG_DFL);
        execvp(argv[1], &argv[1]);
        fprintf(stderr, "failed to exec %s\n", argv[1]);
        exit(-1);
    }
    return 0;
}

Пример вывода:

$ ./sig11 sleep 40
^Cchild 19721 exited with status 0x0002
$

Мы можем обсудить, следует ли вам использовать sigaction() вместо signal() в другой день.

Проверка для handle_signals означает, что еслиПрограмма запускается с игнорированием SIGINT, она не включает сигнал внезапно.Это стандартная техника защиты.Обратите внимание, что ошибки сообщаются при стандартной ошибке, а не при стандартном выводе.Нет необходимости проверять возвращаемое значение из execvp();он не возвращается, если он успешен, и если он действительно возвращается, он окончательно потерпел неудачу.Можно было бы сообщить о лучшей ошибке, используя errno и strerror(), но то, что показано, не является полностью необоснованным.Проверка достаточного количества аргументов является еще одной защитной мерой;сообщить о правильном использовании это хорошие манеры.Я также преобразовал if / else if / else if в if / else if / else + утверждение - третий тест не требуется.


Использование макросов из <sys/wait.h>:

#include <assert.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    if (argc < 2)
    {
        fprintf(stderr, "Usage: %s command [arg ...]\n", argv[0]);
        return 1;
    }
    bool handle_signals = (signal(SIGINT, SIG_IGN) != SIG_IGN);

    pid_t pid = fork();

    if (pid < 0)
    {
        fprintf(stderr, "fork: error\n");
        return 1;
    }
    else if (pid > 0)
    {
        int status;
        int corpse = waitpid(pid, &status, 0);
        printf("child %d exited with status 0x%.4X\n", corpse, status);
        if (WIFEXITED(status))
            printf("child %d exited normally with exit status %d\n", corpse, WEXITSTATUS(status));
        else if (WIFSIGNALED(status))
            printf("child %d exited because of signal %d\n", corpse, WTERMSIG(status));
        else
            printf("child %d neither exited normally nor because of a signal\n", corpse);
    }
    else
    {
        assert(pid == 0);
        if (handle_signals)
            signal(SIGINT, SIG_DFL);
        execvp(argv[1], &argv[1]);
        fprintf(stderr, "failed to exec %s\n", argv[1]);
        exit(-1);
    }
    return 0;
}

Пример выходных данных:

$ ./sig11 sleep 4
child 19839 exited with status 0x0000
child 19839 exited normally with exit status 0
$ ./sig11 sleep 10
^Cchild 19842 exited with status 0x0002
child 19842 exited because of signal 2
$
...