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
Строка 20 осуществляет exec
; если программа доходит до строк 22–23, это указывает на ошибку. Вот что происходит при запуске программы:
$ ch09-run /bin/grep whoami foo /* Запустить grep */
a line /* Входная строка не подходит */
a line with foo in it /* Входная строка подходит */
a line with foo in it /* Это выводится */
^D /* EOF */
$ 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
размещение сигналов изменяется; дополнительные сведения см. в разделе 10.9 «Сигналы для fork()
и exec()
».
После exec
все открытые файлы и каталоги остаются открытыми и доступными для использования. Вот как программы наследуют стандартные ввод, вывод и ошибку: они на месте, когда программа запускается.