Функции семейства exec(), напротив, подменяют исполняемый код текущего процесса (не изменяя его идентификатор PID, права доступа, внешние ресурсы процесса, а также находящийся в том же адресном пространстве) исполняемым кодом из другого файла. Поэтому используются эти вызовы непосредственно после fork() для замены копии вызывающего процесса новым (это классическая UNIX-технология использования).
Функции семейства spawn(), напротив, порождают новый процесс (с новым идентификатором PID и в новом адресном пространстве). Все формы вызовов spawn() после подготовительной работы (иногда очень значительной) в конечном итоге ретранслируются в вызов базовой формы spawn(), который последним действием своего выполнения и посылает сообщение procnto (менеджер процессов QNX, "территориально" объединенный с микроядром системы в одном файле).
Базовый вызов spawn() определяется следующим образом:
#include <spawn.h>
pid_t spawn(const char* path, int fd_count, const int fd_map[],
const struct inheritance* inherit, char* const argv[],
char* const envp[]);
где path - полное имя исполняемого бинарного файла;
fd_count - размерность следующего за ним массива fd_map;
fd_map - массив файловых дескрипторов, которые вы хотели бы унаследовать в дочернем процессе от родительского. Если fd_count не равен 0 (то есть может иметь значения вплоть до константы OPEN_MAX), то fd_map должен содержать список из fd_count файловых дескрипторов. Если же fd_count равен 0, то дочерний процесс наследует все родительские дескрипторы, исключая те, которые созданы с флагом PD_CLOEXEC функции fcntl();
inherit - системная структура (см. системные определения) типа struct inheritance, содержащая как минимум:
unsigned long flags - один или более установленных бит:
SPAWN_CHECK_SCRIPT - позволить spawn() запускать требуемый командный интерпретатор, интерпретируя path как скрипт (интерпретатор указан в первой строке скрипта path);
SPAWN_SEARCH_PATH - использовать переменную окружения PATH для поиска выполняемого файла path;
SPAWN_SETGROUP - установить для дочернего процесса значение группы, специфицируемое членом (структуры) pgroup. Если этот флаг не установлен, дочерний процесс будет частью текущей группы родительского процесса;
SPAWN_SETND - запустить дочерний процесс на удаленном сетевом узле QNET, сам же удаленный узел специфицируется членом (структуры) nd (см. команду удаленного запуска on);
SPAWN_SETSIGDEF - использовать структуру sigdefault для определения процесса множества (набора) сигналов, для которых будет установлена реакция по умолчанию. Если этот флаг не установлен, дочерний процесс наследует все сигнальные реакции родителя;
SPAWN_SETSIGMASK - использовать sigmask в качестве сигнальной маски дочернего процесса.
pid_t pgroup - группа дочернего процесса; имеет смысл, только если установлен флаг SPAWN_SETGROUP. Если флаг SPAWN_SETGROUP установлен и inherit.pgroup установлен как SPAWN_NEWPGROUP, то дочерний процесс открывает новую группу процессов с идентификатором группы (GID), равным PID этого нового процесса.
sigset_t sigmask - сигнальная маска дочернего процесса, если установлен флаг SPAWN_SETSIGMASK.
sigset_t sigdefault - набор сигналов дочернего процесса, для которых определяется реакция по умолчанию, если установлен флаг SPAWN_SETSIGDEF.
uint32_t nd - это совершенно уникальный (относительно других ОС, а значит, и всего POSIX) параметр QNX - дескриптор узла сети QNET, на котором должен быть запущен новый процесс. Это поле используется, только если установлен флаг SPAWN_SETND.
argv - указатель массива аргументов. Значение argv[0] должно быть строкой (char*), содержащей имя файла, загружаемого как процесс (но может быть NULL, если аргументы не передаются). Последний элемент массива argv обязан быть NULL. Само значение argv никогда не может быть NULL.
envp - указатель массива символьных строк переменных системного окружения (environment). Последний элемент массива envp обязан быть NULL. Каждый элемент массива является строкой (char*) вида: variable = value. Если само значение указателя envp равно NULL, то дочерний процесс полностью наследует копию окружения родителя. (Окружение процесса - всегда "копия", поэтому любые изменения, внесенные в окружение дочерним процессом, никак не отражаются на окружении его родителя.)
Примечание
Если дочерний процесс является скриптом интерпретатора (флаг SPAWN_CHECK_SCRIPT), то первая строка текста скрипта должна начинаться с #!, за которыми должны следовать путь и аргументы того интерпретатора, который будет использоваться для интерпретации этого скрипта. К скрипту не применяется установленный в системе интерпретатор по умолчанию (как это происходит при вызове его по имени из командной строки).
Правила наследования (и ненаследования) параметров дочернего процесса от родителя (RID, RGID и других атрибутов) жестко регламентированы, достаточно сложны (в зависимости от флагов) и могут быть уточнены в технической документации QNX. Отметим, что безусловно наследуются такие параметры, как: а) приоритет и дисциплина диспетчеризации; б) рабочий и корневой каталоги файловой системы. Не наследуются: установки таймеров процесса tms_utime, tms_stime, tms_cutime и tms_cstime, значение взведенного сигнала SIGALRM (это значение сбрасывается в ноль), файловые блокировки, блокировки и отображения памяти (shared memory), установленные родителем.
При успешном завершении вызов функции возвращает PID порожденного процесса. При неудаче возвращается -1 и errno устанавливается:
• E2BIG - количество байт, заданное в списке аргументов или переменных окружения и превышающее ARG_MAX;
• EACCESS - нет права поиска в каталогах префикса имени файла, или для файла не установлены права на выполнение, или файловая система по указанному пути была смонтирована с флагом ST_NOEXEC;
• EAGAIN - недостаточно системных ресурсов для порождения процесса;
• ERADF - недопустим хотя бы один из файловых дескрипторов в массиве fd_map;
• EFAULT - недопустима одна из буферных областей, указанных в вызове;
• ELOOP - слишком глубокий уровень символических ссылок к файлу или глубина префиксов (каталогов) в полном пути к файлу;
• EMFILE - недостаточно ресурсов для отображения файловых дескрипторов в дочерний процесс;
• ENAMETOOLONG - длина полного пути превышает PATH_MAX или длина компонента имени файла и пути превышает NAME_MAX;
• ENOENT - файл нулевой длины или несуществующий префиксный компонент в полном пути;
• ENOEXEC - файл, указанный как программа, имеет ошибочный для исполняемого файла формат;
• ENOMEM - в системе недостаточно свободной памяти для порождения процесса;
• ENOSYS - файловая система, специфицированная полным путевым именем файла, не предназначена для выполнения spawn();
• ENOTDIR - префиксные компоненты пути исполняемого файла не являются каталогами;
Даже из этого очень краткого обзора вызова spawn() становятся очевидными некоторые вещи:
• Эта форма универсальна (самодостаточна), она позволяет обеспечить весь спектр разнообразных форм порождения нового процесса