我读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 xz
xz 可以通过检查来判断它是如何被调用的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()
)。
如果./foo
和dir/bar
是指向同一可执行文件的两个不同链接(硬链接或符号链接),则使用这两个路径从 shell 启动程序将分别设置argv[0]
为./foo
和dir/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
此类情况。