Почему первым аргументом execve () должен быть путь к исполняемому файлу - PullRequest
5 голосов
/ 12 июня 2010

Я понимаю, что execve () и family требуют, чтобы первый аргумент массива аргументов совпадал с исполняемым файлом, на который также указывает его первый аргумент. То есть в этом:

execve(prog, args, env);

args [0] обычно совпадает с prog. Но я не могу найти информацию о том, почему это так.

Я также понимаю, что исполняемые файлы (то есть, по крайней мере, сценарии оболочки) всегда имеют путь вызова в качестве первого аргумента при запуске, но я думаю, что оболочка выполнит всю работу, чтобы поместить ее туда, а execve () просто вызовите исполняемый файл, используя путь, указанный в его первом аргументе («prog» сверху), затем передав массив аргументов («args» сверху), как в командной строке .... т.е. я не вызываю скрипты в командной строке с повторяющимся путем к исполняемому файлу в списке аргументов ....

/bin/ls /bin/ls /home/john

Может кто-нибудь объяснить?

Ответы [ 4 ]

10 голосов
/ 12 июня 2010

Нет требования, чтобы первый из аргументов имел какое-либо отношение к имени исполняемого файла:

int main(void)
{
    char *args[3] = { "rip van winkle", "30", 0 };
    execv("/bin/sleep", args);
    return 1;
}

Попробуйте - на Mac (после трех тестов):

make x; ./x & sleep 1; ps

Выход на третий прогон был:

MiniMac JL: make x; ./x & sleep 1; ps
make: `x' is up to date.
[3] 5557
  PID TTY           TIME CMD
 5532 ttys000    0:00.04 -bash
 5549 ttys000    0:00.00 rip van winkle 30
 5553 ttys000    0:00.00 rip van winkle 30
 5557 ttys000    0:00.00 rip van winkle 30
MiniMac JL: 

Комментарии EBM:

Да, и это делает это еще более странным. В моем тестовом скрипте bash (цель execve) я нигде не вижу значения того, что execve имеет в arg [0] - не в среде и не как $ 0.

Пересмотр эксперимента - скрипт с именем 'bash.script':

#!/bin/bash

echo "bash script at sleep (0: $0; *: $*)"
sleep 30

И пересмотренная программа:

int main(void)
{
    char *args[3] = { "rip van winkle", "30", 0 };
    execv("./bash.script", args);
    return 1;
}

Это дает вывод ps:

bash script at sleep (0: ./bash.script; *: 30)
  PID TTY           TIME CMD
 7804 ttys000    0:00.11 -bash
 7829 ttys000    0:00.00 /bin/bash ./bash.script 30
 7832 ttys000    0:00.00 sleep 30

На мой взгляд, есть две возможности:

  1. Ядро выполняет жонглирование командной строки при выполнении сценария через строку shebang ('#!/bin/bash') или
  2. Сам Bash dink со своим списком аргументов.

Как установить разницу? Я полагаю, что копирование оболочки на альтернативное имя, а затем использование этого альтернативного имени в шебанге скажет нам кое-что:

$ cp /bin/bash jiminy.cricket
$ sed "s%/bin/bash%$PWD/jiminy.cricket%" bash.script > tmp
$ mv tmp bash.script
$ chmod +w bash.script
$ ./x & sleep 1; ps
[1] 7851
bash script at sleep (0: ./bash.script; *: 30)
  PID TTY           TIME CMD
 7804 ttys000    0:00.12 -bash
 7851 ttys000    0:00.01 /Users/jleffler/tmp/soq/jiminy.cricket ./bash.script 30
 7854 ttys000    0:00.00 sleep 30
$

Это, я думаю, указывает на то, что ядро ​​переписывает argv[0] при использовании механизма Шебанга.


Обращаясь к комментарию nategoose:

MiniMac JL: pwd
/Users/jleffler/tmp/soq
MiniMac JL: cat al.c
#include <stdio.h>
int main(int argc, char **argv)
{
    while (*argv)
        puts(*argv++);
    return 0;
}
MiniMac JL: make al.c
cc     al.c   -o al
MiniMac JL: ./al a b 'c d' e
./al
a
b
c d
e 
MiniMac JL: cat bash.script
#!/Users/jleffler/tmp/soq/al

echo "bash script at sleep (0: $0; *: $*)"
sleep 30
MiniMac JL: ./x
/Users/jleffler/tmp/soq/al
./bash.script
30
MiniMac JL:

Это показывает, что механизм shebang '#! / Path / to / program', а не любая программа, такая как Bash, регулирует значения argv[0]. Таким образом, когда исполняется двоичный файл, значение argv[0] не корректируется; когда скрипт выполняется через шебанг, список аргументов корректируется ядром; argv[0] - двоичный файл, указанный на шебанге; если после шебанга есть аргумент, он становится argv[1]; следующий аргумент - это имя файла сценария, за которым следуют все оставшиеся аргументы из execv() или эквивалентного вызова.

MiniMac JL: cat bash.script
#!/Users/jleffler/tmp/soq/al -arg0
#!/bin/bash
#!/Users/jleffler/tmp/soq/jiminy.cricket

echo "bash script at sleep (0: $0; *: $*)"
sleep 30
MiniMac JL: ./x
/Users/jleffler/tmp/soq/al
-arg0
./bash.script
30
MiniMac JL: 
4 голосов
/ 12 июня 2010

Позволяет указать точный путь к загружаемому исполняемому файлу, но также позволяет отображать "улучшенное" имя в таких инструментах, как ps или top.

execl("/bin/ls", "ls", "/home/john", (char *)0);
4 голосов
/ 12 июня 2010

Согласно this , первый аргумент, являющийся именем программы, является произвольным.

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

Тем не менее, эти значения могут быть разными.Если, например, программа была запущена по символической ссылке.Имя программы может отличаться от имени ссылки, использованной для ее запуска.

И вы правы.Оболочка обычно выполняет настройку первого аргумента.Однако в этом случае использование execve полностью обходит оболочку, поэтому вам нужно настроить ее самостоятельно.

0 голосов
/ 12 июня 2010

Это позволяет программе иметь много имен и работать немного по-разному, в зависимости от того, какое имя она назвала.

Тривиальная программа обработки изображений, например print0.c, скомпилированная в print0:

#include <stdio.h>
int main(int argc, char **argv)
{
   printf("%s\n",argv[0]);
   return 0;
}

Запуск его как ./print0 приведет к печати ./print0 Создайте символическую ссылку, например print1, и теперь используйте имя ./print1 для его запуска - он напечатает "./print1".

Теперь это было ссимволическая ссылка.Но с помощью функции exec * () вы можете явно указать программе ее имя.

Артефакт из * NIX, но, тем не менее, приятно иметь его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...