• Родитель игнорирует ошибки выполнения вызовов sigaction() и sigprocmask(), с помощью которых изменяются диспозиция и маска сигналов процесса
В стандарте SUSv3 просто говорится, что функция system() должна возвращать –1, если дочерний процесс не удалось создать, иначе его статус не может быть получен. Нет никаких упоминаний о том, что –1 может возвращаться из system() из-за ошибок при операциях с сигналами.
• Для системных вызовов, связанных с сигналами и выполняемых внутри потомка
• При возврате из fork() в потомке диспозицией сигналов SIGINT и SIGQUIT является SIG_IGN (наследуется от родителя). Но, как было отмечено ранее, потомок должен обращаться с этими сигналами так, как будто функция system() сделала вызовы fork() и exec(). Первый не влияет на работу с сигналами в потомке, а второй сбрасывает диспозиции необрабатываемых сигналов до значений по умолчанию, а все остальные сигналы оставляет без изменений (см. раздел 27.5). Следовательно, если диспозиции сигналов SIGINT и SIGQUIT в вызывающем процессе отличаются от SIG_IGN, потомок сбрасывает их к SIG_DFL
Некоторые реализации функции system() вместо сбрасывания диспозиций сигналов SIGINT и SIGQUIT к тем, что имели место в вызывающем процессе, полагаются на тот факт, что последующий вызов execl() автоматически сбросит диспозиции обрабатываемых сигналов к значениям по умолчанию. Однако это может привести к нежелательным последствиям, если любой из этих сигналов обрабатывается в вызывающем процессе. В таком случае, если сигнал был доставлен потомку перед вызовом execl(), обработчик будет вызван внутри потомка, но после того, как вызов sigprocmask() разблокирует сигнал.
• Если вызов execl() в потомке заканчивается неудачей, мы завершаем процесс с помощью _exit()
• Родитель должен использовать вызов waitpid(), чтобы отслеживать именно того потомка, которого мы создали
• Реализация system() не требует использования обработчиков сигналов, однако такие обработчики могут быть установлены вызывающей программой, из-за чего может прерваться работа заблокированного вызова waitpid(). В стандарте SUSv3 явно обозначено, что операция ожидания в этом случае должна быть запущена заново. Поэтому waitpid() вызывается в цикле, который заканчивает свою работу при любой ошибке, кроме EINTR
Листинг 27.9. Реализация функции system()
procexec/system.c
#include
#include
#include
#include
#include
int
system(const char *command)
{
sigset_t blockMask, origMask;
struct sigaction saIgnore, saOrigQuit, saOrigInt, saDefault;
pid_t childPid;
int status, savedErrno;
return system(":") == 0;
sigemptyset(&blockMask); /* Блокируем SIGCHLD */
sigaddset(&blockMask, SIGCHLD);
saIgnore.sa_handler = SIG_IGN; /* Игнорируем SIGINT и SIGQUIT */
saIgnore.sa_flags = 0;
sigemptyset(&saIgnore.sa_mask);
sigaction(SIGQUIT, &saIgnore, &saOrigQuit);
switch (childPid = fork()) {
case –1: /* Вызов fork() завершился неудачей */
status = –1;
break; /* Идем дальше, чтобы сбросить атрибуты сигнала */
case 0: /* Потомок запускает команду */
saDefault.sa_handler = SIG_DFL;
saDefault.sa_flags = 0;
sigemptyset(&saDefault.sa_mask);
sigaction(SIGINT, &saDefault, NULL);
if (saOrigQuit.sa_handler!= SIG_IGN)
sigaction(SIGQUIT, &saDefault, NULL);
execl("/bin/sh", "sh", "-c", command, (char *) NULL);