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

时间:2010-06-12 03:08:28

标签: c exec

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

execve(prog, args, env);

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

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

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

有人可以解释一下吗?

4 个答案:

答案 0 :(得分:10)

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

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本身与其参数列表相关联。
  3. 如何建立差异?我想将shell复制到另一个名称,然后在shebang中使用那个替代名称会告诉我们一些事情:

    $ 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
    $
    

    我认为这表明当使用shebang机制时内核会重写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]的值不会被调整;当通过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: 
    

答案 1 :(得分:4)

根据this,第一个参数是程序名称是自定义。

  

按自定义,第一个元素应该是   已执行程序的名称(for   例如,路径的最后一个组成部分

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

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

答案 2 :(得分:4)

它允许您指定要加载的可执行文件的确切路径,但也允许在pstop等工具中显示“美化”名称。

execl("/bin/ls", "ls", "/home/john", (char *)0);

答案 3 :(得分:0)

这允许程序具有许多名称,并且根据使用的名称而略有不同。

成像琐碎程序,例如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的神器,不过很高兴。