为什么首先arc to execve()必须是可执行文件的路径

EBM*_*EBM 5 c exec

据我所知,execve()和family要求其参数数组的第一个参数与其第一个参数也指向的可执行文件相同.就是这样:

execve(prog, args, env);
Run Code Online (Sandbox Code Playgroud)

args [0]通常与prog相同.但我似乎无法找到有关其原因的信息.

我也明白可执行文件(呃,至少是shell脚本)在运行时总是把它们的调用路径作为第一个参数,但是我认为shell会把它放在那里,而execve()只会调用可执行文件使用第一个参数中给出的路径(上面的"prog"),然后在命令行上传递参数数组("args"从上面)....即,我不在命令上调用脚本args列表中的重复可执行路径行....

/bin/ls /bin/ls /home/john
Run Code Online (Sandbox Code Playgroud)

谁能解释一下?

Jon*_*ler 10

不要求第一个参数与可执行文件的名称有任何关系:

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

试试吧 - 在Mac上(三次测试后):

make x; ./x & sleep 1; ps
Run Code Online (Sandbox Code Playgroud)

第三轮的输出是:

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: 
Run Code Online (Sandbox Code Playgroud)

EBM评论:

Yeah, and this makes it even more weird. In my test bash script (the target of the execve), I don't see the value of what execve has in arg[0] anywhere -- not in the environment, and not as $0.

Revising the experiment - a script called 'bash.script':

#!/bin/bash

echo "bash script at sleep (0: $0; *: $*)"
sleep 30
Run Code Online (Sandbox Code Playgroud)

And a revised program:

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

This yields the ps output:

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
Run Code Online (Sandbox Code Playgroud)

There are two possibilities as I see it:

  1. The kernel juggles the command line when executing the script via the shebang ('#!/bin/bash') line, or
  2. Bash itself dinks with its argument list.

How to establish the difference? I suppose copying the shell to an alternative name, and then using that alternative name in the shebang would tell us something:

$ 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
$
Run Code Online (Sandbox Code Playgroud)

This, I think, indicates that the kernel rewrites argv[0] when the shebang mechanism is used.


Addressing the comment by 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:
Run Code Online (Sandbox Code Playgroud)

这表明shebang'#!/ path/to/program'机制,而不是任何程序,如Bash,调整的值argv[0].因此,当执行二进制时,argv[0]不调整值; 当通过shebang执行脚本时,参数列表由内核调整; argv[0]是在shebang上列出的二进制文件; 如果在shebang之后有争执,那就变成了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: 
Run Code Online (Sandbox Code Playgroud)


Ign*_*ams 6

它允许您指定要加载的可执行文件的确切路径,但也允许在诸如ps或 之类的工具中显示“美化”名称top

execl("/bin/ls", "ls", "/home/john", (char *)0);
Run Code Online (Sandbox Code Playgroud)

  • 一些程序还使用 arg[0] 来确定要做什么:例如,busybox 就是这样做的。我相信 bash 也会改变它的行为,这取决于你调用它的方式。(`bash` 或只是 `sh`) (3认同)
  • 你需要双引号;并且可能是一个空字符 * 来终止参数列表,假设您正在使用 execl()。 (2认同)

Tim*_*ane 5

根据这个,第一个参数是程序名称是一种习俗。

按照习惯,第一个元素应该是执行程序的名称(例如,路径的最后一个组件)

也就是说,这些值可能不同。例如,如果程序是从符号链接启动的。程序名称可能与用于启动它的链接的名称不同。

而且,你是对的。shell 通常会完成设置第一个参数的工作。然而,在这种情况下, execve 的使用完全绕过了 shell - 这就是为什么你需要自己设置它。