为什么在execve中需要将命令名作为路径和参数列表两次传递?

8

我有一个由我的教授编写的程序,它使用execve()打印当前工作目录(pwd),但是我不理解其中的参数。

pid_t pid = fork();

if(pid <0)
   perror(NULL);
else if(pid == 0)
{
   char*argv[] = {"pwd",NULL};
   execve("/bin/pwd",argv,NULL);
   perror(NULL);
}
else
    printf("Im the parent!");
return 0;
}

"/bin/pwd" 提供将要执行的可执行文件的路径。

这意味着它将调用 pwd 函数,不是吗? 那么我为什么需要具有参数 pwd 呢?

程序不能在没有该参数的情况下运行吗

3个回答

5

按照惯例,传递给程序的第一个参数是可执行文件的文件名。然而,并非必须如此。

例如,考虑下面的程序:

#include <stdio.h>

int main(int argc, char *argv[])
{
    int i;

    printf("number of arguments: %d\n", argc);
    printf("program name: %s\n", argv[0]);
    for (i=1; i<argc; i++) {
        printf("arg %d: %s\n", argv[i]);
    }
    return 0;
}

如果您把这个程序从其他地方运行,代码会像这样:
char*argv[] = {"myprog", "A", "B", NULL};
execve("/home/dbush/myprog",argv,NULL);

以上将输出:
number of arguments: 3
program name: myprog
arg 1: A
arg 2: B

但你也可以这样运行它。
char*argv[] = {"myotherprog", "A", "B", NULL};
execve("/home/dbush/myprog",argv,NULL);

并且它将输出:

number of arguments: 3
program name: myotherprog
arg 1: A
arg 2: B

你可以使用 argv [0] 的值来知道程序被调用的方式,并基于此公开不同的功能。
流行的 busybox 工具就是这样做的。一个可执行文件链接了不同的文件名。根据用户用于运行可执行文件的链接,它可以读取 argv[0] 来知道它是作为 lspspwd 等来调用的。

1
一个很好的比较是HTTP的“Host”头,它为网站执行完全相同的操作。 - that other guy
当我们输入 pwd 命令时,它会在下一行打印出当前工作目录,但是在 C 程序中调用 /bin/pwd/ 命令会在终端的第一行打印出当前工作目录。 - EsmaeelE

3
execve手册页面提到了这一点。其中强调是我的。
引用:
按照惯例,第一个字符串应该包含与正在执行的文件相关联的文件名。
也就是说,第一个argv并不一定非得是文件名。实际上,可以通过将示例代码中的argv [0]更改为任何字符串来测试,并且结果仍然是正确的。
因此,这只是一种约定。许多程序将使用argv [0]并期望它是文件名。但是,许多程序也不关心argv [0](例如pwd)。因此,是否需要将argv [0]设置为文件名取决于正在执行的程序。话虽如此,始终遵循惯例以与几乎所有人的长期期望保持良好关系是明智的。

0

来自execve手册页面:http://man7.org/linux/man-pages/man2/execve.2.html

   argv is an array of argument strings passed to the new program.  By
   convention, the first of these strings (i.e., argv[0]) should contain
   the filename associated with the file being executed.  envp is an
   array of strings, conventionally of the form key=value, which are
   passed as environment to the new program.  The argv and envp arrays
   must each include a null pointer at the end of the array.

所以,argv 被视为新程序执行的命令行参数。 因为默认情况下,对于使用参数调用的 Linux 二进制文件,这些参数是通过 argc/argv 访问的,其中 argv[0] 包含程序名称。
我认为这是为了保持行为一致,以匹配默认情况(使用参数调用的程序)。
从源代码中可以看到: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/exec.c#l1376 传递给 execveargv 用于构建将要启动的二进制文件的 argv

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接