Значение из 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
$