• Родитель игнорирует ошибки выполнения вызовов sigaction() и sigprocmask(), с помощью которых изменяются диспозиция и маска сигналов процесса , , . Это делается по двум причинам. Во-первых, эти вызовы крайне редко завершаются неудачей. Но единственная проблема, которая действительно может с ними возникнуть, — некорректное задание аргументов, от которого нужно избавляться еще на стадии начальной отладки. Во-вторых, мы предполагаем, что вызывающий процесс больше заинтересован в получении сведений об ошибках, связанных с вызовами fork() или waitpid(). По схожим причинам вызовы для работы с сигналами в конце system() помещаются между строчками кода, сохраняющими и восстанавливающими значение errno ; если вызовы fork() или waitpid() завершатся неудачей, вызывающий процесс сможет определить источник ошибки. Если в результате неудачных вызовов для работы с сигналами вернуть –1, вызывающий процесс может ошибочно предположить, что функции system() не удалось выполнить команду.

В стандарте SUSv3 просто говорится, что функция system() должна возвращать –1, если дочерний процесс не удалось создать, иначе его статус не может быть получен. Нет никаких упоминаний о том, что –1 может возвращаться из system() из-за ошибок при операциях с сигналами.

• Для системных вызовов, связанных с сигналами и выполняемых внутри потомка , , проверка на ошибки не проводится. С другой стороны, о таких ошибках просто невозможно сообщить (вызов _exit(127) зарезервирован для ошибок, происходящих во время работы командной оболочки); однако стоит отметить, что такие ошибки не затрагивают вызывающий 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() вместо exit(), чтобы предотвратить сброс на диск любых незаписанных данных из дочерней копии буферов stdio.

• Родитель должен использовать вызов waitpid(), чтобы отслеживать именно того потомка, которого мы создали . Если бы мы прибегли к вызову wait(), родитель мог бы случайно извлечь статус какого-нибудь другого созданного им дочернего процесса.

• Реализация 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;

if (command == NULL) /* Доступна ли командная оболочка? */

return system(":") == 0;

sigemptyset(&blockMask); /* Блокируем SIGCHLD */

sigaddset(&blockMask, SIGCHLD);

sigprocmask(SIG_BLOCK, &blockMask, &origMask);

saIgnore.sa_handler = SIG_IGN; /* Игнорируем SIGINT и SIGQUIT */

saIgnore.sa_flags = 0;

sigemptyset(&saIgnore.sa_mask);

sigaction(SIGINT, &saIgnore, &saOrigInt);

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);

 if (saOrigInt.sa_handler!= SIG_IGN)

sigaction(SIGINT, &saDefault, NULL);

if (saOrigQuit.sa_handler!= SIG_IGN)

sigaction(SIGQUIT, &saDefault, NULL);

 sigprocmask(SIG_SETMASK, &origMask, NULL);

execl("/bin/sh", "sh", "-c", command, (char *) NULL);

 _exit(127); /* Нам не удалось запустить командную оболочку */

Перейти на страницу:

Похожие книги