Почему мы должны дважды передавать имя файла в функции exec?

11

Я прочитал « Расширенное программирование в среде UNIX » Стивенса, 8- я глава. Я прочитал и понял все шесть функций exec.

Во всех функциях exec я заметил одну вещь:

  • Первый аргумент - это имя файла / путь (зависит от функции exec).
  • Второй аргумент - это argv [0], который мы получаем main(), это само имя файла.

Здесь мы должны дважды передать имя файла в функции.

Есть ли для этого какая-либо причина (например, мы не можем получить имя файла по пути из первого аргумента)?

munjal007
источник

Ответы:

14

Здесь мы должны дважды передать имя файла в функции.

Они не совсем то же самое, что вы заметили, заметив, что один из них используется в качестве argv[0]значения. Это не должно совпадать с базовым именем исполняемого файла; многие / большинство вещей игнорируют это, и вы можете положить туда все, что захотите.

Первый - это фактический путь к исполняемому файлу, для которого есть очевидная необходимость. Второй передается процессу якобы как имя, используемое для его вызова, но, например:

execl("/bin/ls", "banana", "-l", NULL);

Будет работать нормально, если предположить, что /bin/lsэто правильный путь.

Некоторые приложения, однако, используют argv[0]. Обычно они имеют одну или несколько символических ссылок $PATH; это распространено в утилитах сжатия (иногда они используют оболочки оболочки). Если вы xzустановили, stat $(which xzcat)показывает, что это ссылка xz, и man xzcatэто то же самое, man xzчто объясняет, что «xzcat эквивалентен xz --decompress --stdout». Способ, которым xz может сказать, как он был вызван, - проверка argv[0], делающая эти эквиваленты:

execl("/bin/xz", "xzcat", "somefile.xz", NULL);
execl("/bin/xz", "xz", "--decompress", "--stdout", "somefile.xz", NULL);
лютик золотистый
источник
4
Ах, так это объясняет, как busyboxможет быть то, что вы хотите, чтобы это было в зависимости от того, как вы это называете правильно?
Terdon
3
@terdon, именно так один двоичный файл для busybox удовлетворяет такому количеству различных команд.
ма
7
Что означало бы, что если бы /bin/lsбыл busybox, он не знал бы, как выполнить banana!
Riking
6

Вам не нужно передавать имя файла дважды.

Первый - это файл, который на самом деле исполняется.

Вторым аргументом является то, каким должен быть argv[0]процесс, то есть то, что процесс должен видеть в качестве своего имени. Например, если вы запускаете lsиз оболочки, первый аргумент /bin/ls, а второй просто ls.

Вы можете выполнить определенный файл и вызвать его как-нибудь через второй аргумент; программа может проверить свое имя и вести себя по-разному в зависимости от имени. Это также может быть сделано через жесткие ссылки (или символические ссылки), но этот способ дает большую гибкость.

wurtel
источник
На самом деле ссылки - это один и тот же метод, поскольку он устанавливает argv[0]имя ссылки.
Златовласка
В последнем абзаце «Вы можете выполнить определенный файл и вызвать его как-нибудь через второй аргумент; программа может проверить его имя и вести себя« по-другому »в соответствии с именем». Можете ли вы уточнить или дать мне некоторые чтения, я новичок в этой среде.
munjal007
Последняя часть ответа Златовласки объясняет это.
wurtel
1

Вывод, который argv[0]может быть установлен на что угодно (в том числе NULL). По соглашению , argv[0]будет установлен путь, по которому исполняемый файл был запущен (процессом оболочки, когда он это делает execve()).

Если ./fooи dir/barэто две разные ссылки (жесткие или символические) в том же исполняемый файл, а затем запустить программу из командной строки с помощью двух путей будет установлен argv[0]в ./fooи dir/bar, соответственно.

То, что argv[0]может быть NULL, часто упускается из виду. NULL argv[0]Например, следующий код может привести к сбою (хотя вместо этого glibc печатает что-то вроде <null>argv[0] ):

if (argc != 3) {
    fprintf(stderr, "%s: expected 2 arguments\n", argv[0]);
    exit(EXIT_FAILURE);
}

Альтернативой в Linux является использование /proc/self/exeдля таких случаев.

Ulfalizer
источник
как вы можете установить argv [0] для обоих ./foo и dir / bar
munjal007
@ munjal007 Извините, если мне было неясно. Я имел в виду запуск программы дважды: один раз как ./fooи один раз как dir/bar. argv[0]будет отличаться для этих двух случаев (в каждом случае он будет таким же, как путь, который вы использовали).
Ульфализатор
@ munjal007 Предполагается, что вы запускаете его из оболочки, конечно. Дело в том, что вы можете установить argv[0]что угодно, когда exec*()сами программируете. Это оболочка, которая устанавливает argv[0]путь, который использовался для запуска программы (и разумно делать то же самое, когда вы exec*()работаете с программой, поскольку многие программы проверяют argv[0]и ожидают, что она будет содержать путь).
Ульфализатор