为什么我们必须在 exec 函数中传递文件名两次?

为什么我们必须在 exec 函数中传递文件名两次?

我读UNIX 环境中的高级编程作者:史蒂文斯,8章。我阅读并理解了所有六个 exec 函数。

我注意到的一件事是,在所有 exec 函数中:

  • 第一个参数是文件名/路径名(取决于 exec 函数)。
  • 第二个参数是我们得到的 argv[0] main(),它是文件名本身。

所以这里我们必须在函数中传递文件名两次。

有什么原因吗(就像我们无法从第一个参数的路径名中获取文件名)?

答案1

所以这里我们必须在函数中传递文件名两次。

它们与您通过观察注意到的并不完全相同其中的一个被用作值argv[0]。这不必与可执行文件的基本名称相同;很多/大多数事情都会忽略它,你可以把你想要的任何东西放在那里。

第一个是可执行文件的实际路径,这显然是必要的。第二个表面上作为用于调用它的名称传递给进程,但是,例如:

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

/bin/ls假设是正确的路径,就可以正常工作。

然而,某些应用程序确实使用argv[0].通常这些在 ; 中有一个或多个符号链接$PATH。这在压缩实用程序中很常见(有时它们使用外壳包装器代替)。如果您已xz安装,则stat $(which xzcat)显示它是 的链接xz,并且与解释“xzcat 相当于 xz --decompress --stdout”的内容man xzcat相同。 man xzxz 可以通过检查来判断它是如何被调用的argv[0],使它们等效:

execl("/bin/xz", "xzcat", "somefile.xz", NULL);
execl("/bin/xz", "xz", "--decompress", "--stdout", "somefile.xz", NULL);

答案2

您不必传递文件名两次。

第一个是实际执行的文件。

第二个参数是进程的名称argv[0],即进程的名称。例如,如果您ls从 shell 运行,第一个参数是/bin/ls,第二个参数是ls

您可以执行某个文件并通过第二个参数将其称为其他名称;程序可以检查其名称并根据名称进行不同的行为。这也可以通过硬链接(或符号链接)来完成,但这种方式提供了更大的灵活性。

答案3

要点是argv[0]可以设置为任何值(包括NULL)。按照惯例argv[0]将被设置为可执行文件启动时的路径(由 shell 进程执行 时execve())。

如果./foodir/bar是指向同一可执行文件的两个不同链接(硬链接或符号链接),则使用这两个路径从 shell 启动程序将分别设置argv[0]./foodir/bar

事实上,这一点argv[0]常常NULL被忽视。例如,以下代码可能会崩溃NULL argv[0](尽管 glibc 打印类似<空>而对于argv[0]):

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

Linux 上的另一种选择是用于/proc/self/exe此类情况。

相关内容