20 execv(path, argv + 2); /* skip argv[0] and argv[1] */
21
22 fprintf(stderr, "%s: execv() failed: %s\n", argv[0],
23 strerror(errno));
24 exit(1);
25 }
Первый аргумент является путем к запускаемой программе, а второй аргумент является новым именем для программы (которое большинство утилит игнорируют, кроме сообщений об ошибках); все остальные аргументы передаются вызываемой программе.
Строки 13–16 осуществляют проверку ошибок. Строка 18 сохраняет путь в path
exec
; если программа доходит до строк 22–23, это указывает на ошибку. Вот что происходит при запуске программы:$ ch09-run /bin/grep whoami foo
a line
a line with foo in it
a line with foo in it
^D
$ ch09-run nonexistent-program foo bar
ch09-run: execv() failed: No such file or directory
Следующий пример несколько неестественен: мы заставили ch09-run
foo
'. Поскольку аргументов для второго запуска недостаточно, она выводит сообщение об использовании и завершается:$ ch09-run ./ch09-run foo
usage: foo path arg() [ arg ... ]
Хотя она и не очень полезна, ch09-run
argv[0]
не обязательно должен иметь какое-нибудь отношение к файлу, который в действительности запускается.В System III (примерно в 1980-м) команды cp
ln
и mv
представляли один исполняемый файл с тремя ссылками с этими именами в /bin
. Программа проверяла argv[0]
и решала, что она должна делать. Это сохраняло некоторое количество дискового пространства за счет усложнения исходного кода и форсирования выполнения программой действия по умолчанию при запуске с неизвестным именем. (Некоторые современные коммерческие системы Unix продолжают эту практику!) Без явной формулировки причин gmake
, gawk
и т.д. Если такие программы ожидают лишь стандартные имена, они при запуске с другим именем потерпят неудачу.Сегодня также дисковое пространство дешево; если из одного и того же исходного кода можно построить две почти идентичные программы, лучше это сделать, использовав #ifdef
grep
и egrep
имеют значительную часть общего кода, но GNU версия строит два отдельных исполняемых файла.9.1.4.4. Атрибуты, наследуемые exec()
Как и в случае с fork()
exec
сохраняется ряд атрибутов:• Все открытые файлы и открытые каталоги; см. раздел 4.4.1 «Понятие о дескрипторах файлов» и раздел 3.3.1 «Базовое чтение каталогов». (Сюда не входят файлы, помеченные для закрытия при исполнении (close-on-exec), как описано далее в этой главе; см. раздел 9.4.3.1 «Флаг close-on-exec».)
• Установки umask; см. раздел 4.6 «Создание файлов».
• Текущий рабочий каталог, см. раздел 8.4.1 «Изменение каталога: chdir()
fchdir()
»• Корневой каталог; см. раздел 8.6 «Изменение корневого каталога: chroot()
• Текущее значение относительного приоритета.
• ID процесса и ID родительского процесса.
• ID группы процесса и контролирующий терминал; см. раздел 9.2.1 «Обзор управления работами».
• Маску сигналов процесса и любые ожидающие сигналы, а также любые не истекшие аварийные сигналы или таймеры (здесь не обсуждается; см. главу 10 «Сигналы»).
• Действительные ID пользователя и ID группы, а также дополнительный набор групп. Эффективные ID пользователя и группы (а следовательно, и сохраненные ID set-user и set-group) могут быть установлены с помощью битов setuid и setgid исполняемого файла. (Ничто из этого пока не обсуждалось; см. главу 11 «Права доступа и ID пользователя и группы».)
• Блокировки файлов сохраняются (также пока не обсуждалось; см. раздел 14.2 «Блокировка файлов»).
• Суммарное использованное время процессора для процесса и его потомков не меняется.
После exec
fork()
и exec()
».После exec