• EACCES — аргумент pathname не указывает на обычный файл, файл не является исполняемым или запрещено чтение содержимого одного из каталогов, являющегося частью pathname (то есть для каталога не выставлен признак «на исполнение»). Как вариант, файл может храниться в файловой системе, подключенной с использованием флага MS_NOEXEC (см. подраздел 14.8.1).
• ENOENT — файл, на который ссылается pathname, не существует.
• ENOEXEC — файл, на который ссылается pathname, помечен как исполняемый, но система не распознает его формат. Возможно, это скрипт, в первой строке которого не был указан интерпретатор (такая строка должна начинаться с символов #!).
• ETXTBSY — файл, на который ссылается pathname, открыт для записи другим процессом (см. подраздел 4.3.2).
• E2BIG — общий объем памяти, требуемый для хранения списка аргументов и переменных среды, превышает допустимое ограничение.
Ошибки, перечисленные выше, могут также возникнуть в случае, если любое из этих условий будет исполнено для интерпретатора, который должен выполнить скрипт (см. раздел 27.3), или для ELF-интерпретатора, с помощью которого выполняется программа.
Формат ELF (Executable and Linking Format) является широко распространенной спецификацией, описывающей структуру исполняемый файлов. Обычно во время выполнения образ процесса строится на основе сегментов исполняемого файла (см. раздел 6.3). Однако спецификация ELF также позволяет указать интерпретатор (заголовочный элемент PT_INTERP) для выполнения программы. Если интерпретатор определен, ядро строит образ процесса из сегментов указанного исполняемого файла интерпретатора. Затем уже сам интерпретатор должен загрузить и выполнить программу. Больше о роли интерпретаторов в формате ELF можно узнать в главе 41; там же будут даны отсылки к более углубленной информации.
Пример использования вызова execve() показан в листинге 27.1. Данный код создает списки аргументов и переменных среды для новой программы и затем вызывает execve(), задействуя в качестве пути к исполняемому файлу аргумент командной строки (argv[1]).
В листинге 27.2 приводится программа, которая должна быть запущена кодом из предыдущего листинга. Все, что она делает, — это выводит аргументы командной строки и переменные среды (доступ к последним осуществляется посредством глобальной переменной environ, как описывается в разделе 6.7).
Работа программ из листингов 27.1 и 27.2 показана в следующей сессии командной оболочки (в этом примере для задания исполняемого файла используется относительный путь):
$ ./t_execve./envargs
argv[0] = envargs
argv[1] = hello world
argv[2] = goodbye
environ: GREET=salut
environ: BYE=adieu
Листинг 27.1. Использование вызова execve() для запуска новой программы
procexec/t_execve.c
#include "tlpi_hdr.h"
int
main(int argc, char *argv[])
{
char *argVec[10]; /* С запасом */
char *envVec[] = { "GREET=salut", "BYE=adieu", NULL };
if (argc!= 2 || strcmp(argv[1], "-help") == 0)
usageErr("%s pathname\n", argv[0]);
argVec[0] = strrchr(argv[1], '/');
/* Получаем последнюю часть имени файла из argv[1] */
if (argVec[0]!= NULL)
argVec[0]++;
else
argVec[0] = argv[1];
argVec[1] = "hello world";
argVec[2] = "goodbye";
argVec[3] = NULL; /* В конце списка должно быть значение NULL */
execve(argv[1], argVec, envVec);
errExit("execve"); /* Если мы сюда добрались, что-то пошло не так */
}
procexec/t_execve.c
Листинг 27.2. Вывод списка аргументов и переменных среды
procexec/envargs.c
#include "tlpi_hdr.h"
extern char **environ;
int
main(int argc, char *argv[])
{
int j;
char **ep;
for (j = 0; j < argc; j++)
printf("argv[%d] = %s\n", j, argv[j]);
for (ep = environ; *ep!= NULL; ep++)
printf("environ: %s\n", *ep);
exit(EXIT_SUCCESS);
}
procexec/envargs.c
Библиотечные функции, описанные в этом разделе, предоставляют альтернативный интерфейс к операции exec(). Все они работают поверх вызова execve(), отличаются от него и различаются между собой только тем, каким образом указываются имя программы, список аргументов и переменные среды.
#include
int execle(const char *
/*, (char *) NULL, char *const
int execlp(const char *
/*, (char *) NULL */);
int execvp(const char *
int execv(const char *
int execl(const char *
/*, (char *) NULL */);
Ни одна из этих функций не возвращает результат при успешном выполнении; при ошибке все они возвращают –1