flags |= FD_CLOEXEC; /* Устанавливаем FD_CLOEXEC */

if (fcntl(STDOUT_FILENO, F_SETFD, flags) == –1) /* Обновляем флаги */

errExit("fcntl — F_SETFD");

}

execlp("ls", "ls", "-l", argv[0], (char *) NULL);

errExit("execlp");

}

procexec/closeonexec.c

27.5. Сигналы и вызов exec()

Во время выполнения вызова exec() текст существующего процесса удаляется. Этот текст мог содержать обработчики сигналов, установленные вызывающей программой. Поскольку обработчики исчезли, ядро изменяет диспозиции для всех перехватываемых сигналов на SIG_DFL. Диспозиции всех остальных сигналов (то есть SIG_IGN или SIG_DFL) остаются без изменений. Это требование стандарта SUSv3.

В стандарте SUSv3 игнорирование сигнала SIGCHLD оговаривается отдельно (в подразделе 26.3.3 уже отмечалось, что это позволяет избежать возникновения процессов-«зомби»). Однако в нем не уточняется, игнорируется ли данный сигнал на протяжении всего вызова exec(), или же его действие сбрасывается к SIG_DFL. В Linux применяется первый вариант, но в некоторых реализациях UNIX (таких как Solaris) происходит сбрасывание. Из этого следует, что для достижения максимальной переносимости программ, которые игнорируют сигнал SIGCHLD, нам следует выполнять signal(SIGCHLD, SIG_DFL) перед вызовом exec() и исходить из того, что исходной диспозицией этого сигнала является SIG_DFL.

Удаление данных, кучи и стека старой программы также означает, что любой альтернативный стек сигналов, полученный в результате вызова sigaltstack() (см. раздел 21.3), теряется. Поскольку этот стек не сохраняется во время выполнения exec(), бит SA_ONSTACK тоже сбрасывается для всех сигналов.

Во время выполнения exec() у процесса сохраняются сигнальная маска и сигналы, находящиеся в состоянии ожидания. Это позволяет блокировать и складывать в очередь сигналы для новой программы. Однако в стандарте SUSv3 отмечается, что многие существующие приложения ошибочно предполагают, что определенные сигналы при их запуске изначально имели диспозицию SIG_DFL или были разблокированными (в частности, стандарты языка С содержат куда менее конкретную спецификацию сигналов, в которой не упоминается их блокирование; следовательно, программы на этом языке, написанные на несовместимых с UNIX системах, не знают о том, что сигналы нужно разблокировать). По этой причине в стандарте SUSv3 рекомендуется не блокировать и не игнорировать сигналы во время выполнения сторонних программ с помощью exec(). Под сторонними подразумеваются программы, написанные не нами. Если приложение разработали мы сами или если мы знаем, как оно ведет себя с сигналами, от этого правила можно отойти.

27.6. Выполнение консольных команд: system()

Функция system() позволяет вызывающей программе выполнять произвольные консольные команды. В этом разделе мы опишем принцип ее работы, а в следующем — покажем, как ее можно реализовать с помощью вызовов fork(), exec(), wait() и exit().

В разделе 44.5 рассматриваются функции popen() и pclose(), которые тоже могут быть использованы для вызова консольных команд, но при этом позволяют вызывающей программе либо считывать вывод команды, либо отправлять ей ввод.

#include

int system(const char *command);

Описание возвращаемого значения ищите в основном тексте

Функция system() создает дочерний процесс для выполнения команды command. Ниже показан пример ее вызова:

system("ls | wc");

Принципиальными преимуществами функции system() являются простота и удобство.

• Нам не нужно иметь дело с деталями вызовов fork(), exec(), wait() и exit().

• Обработка ошибок и сигналов выполняется за нас самой функцией system().

• Поскольку system() использует для выполнения команды командную оболочку, перед ее запуском выполняются все стандартные процедуры обработки, подстановки и перенаправления. Это упрощает добавление в приложение возможности вида «выполнить консольную команду» (подобная функция доступна во многих интерактивных программах по команде!).

Главным недостатком функции system() является ее низкая производительность. Запуск команды с ее помощью требует создания как минимум двух процессов — одного для командной оболочки, и еще одного — для выполняемой команды; каждый такой процесс требует вызова exec(). Если эффективность или скорость являются важным требованием, для запуска нужной программы лучше воспользоваться непосредственно вызовами fork() и exec().

Значение, возвращаемое функцией system(), зависит от следующих факторов.

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

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