21

我知道execvp可以用来执行简单的命令如下:

char* arg[] = {"ls", "-l", NULL};
execvp(arg[0],arg);

我想知道我跑步时这里发生了什么execvp。在手册页中,它说execvp用新的图像替换过程图像的图像。但是在这里我正在运行一个命令而不是可执行文件。

具体来说,假设有一个命令特别需要输入,例如 cat。如果我有一个包含 cat 预期文件名的文本文件 text.txt 并且我将 stdin 重定向到文件的文件流,则execle("cat","cat",NULL)or的输出execvp("cat", arg)(显然是 arg 存储"cat"和的位置NULL)会导致控制台中的输出为cat /filename将?我的直觉是我必须阅读文件并可能解析它以将参数存储在 arg 中。不过我想确定一下。

提前致谢!

4

4 回答 4

20

以下是通话中发生的情况execvp

  1. 如果适用,您的 libc 实现会在 中搜索PATH要执行的文件。大多数(如果不是全部)类 UNIX 系统中的命令都是可执行的。如果不是,会发生什么?尝试一下。看看glibc 是如何做到的。
  2. 通常,如果找到可执行文件,将调用execve。部分execve可以在 libc 中实现,也可以是系统调用(如在 Linux 中)。
  3. Linux 通过为程序分配内存、打开程序、调度执行、初始化内存结构、根据调用提供的参数设置其参数和环境execvp、找到适合加载二进制文件的处理程序以及设置当前任务来准备程序(execvp调用者)没有执行。你可以在这里找到它的实现。

上述所有步骤都符合相关手册页中描述的 POSIX 设置的要求。

于 2013-01-13T07:56:09.563 回答
13

关于你的问题:

在手册页中,它说execvp用新的图像替换过程图像的图像。但是在这里我正在运行一个命令而不是可执行文件。

很久以前,shell 非常有限,几乎所有 UNIX 命令都是独立的可执行文件。现在,主要是出于速度目的,一些 UNIX 命令子集在 shell 本身内部实现,这些命令称为builtins. 您可以通过命令检查在您的 shell 中实现的任何命令是否是内置的type

λ ~/ type echo
echo is a shell builtin

(带有描述的内置函数的完整列表可以在man您的 shell 页面中找到,例如man bash-builtinsman builtin。)

但是大多数命令仍然有它们的可执行对应项:

λ ~/ whereis echo
/bin/echo

因此,在您运行时的特定情况下:

char* arg[] = {"ls", "-l", NULL};
execvp(arg[0],arg);

您实际上是用 (最有可能) 的地址空间替换当前进程的地址空间/bin/ls


我的直觉是我必须阅读文件并可能解析它以将参数存储在 arg 中。

确实你有。但是您也可以为此使用一些内核函数,即“shebang”:
不要将文件名放在单独的文件中,而是添加所谓的 shebang 作为您想要 cat 的文件的第一行:

#!/bin/cat

并添加chmod +x到它。然后您可以将其作为可执行文件运行(通过任何exec函数或 shell):

λ ~/tmp/ printf '#!/bin/cat\nTEST\n' > cat_me
λ ~/tmp/ chmod +x cat_me
λ ~/tmp/ ./cat_me 
#!/bin/cat
TEST

当然,它有一个shebang用文件打印自身的缺点,但在内核中执行它仍然很有趣=)

顺便提一句。您描述的问题很常见,以至于有一个称为特殊可执行文件xargs(在非常简化的解释中)在通过标准输入传递的参数列表上执行给定程序。欲了解更多信息,请咨询man xargs


为了便于记忆exec-family,我经常使用下表:

           Figure 8.14. Differences among the six exec functions
+----------+----------+----------+----------+--------+---------+--------+
| Function | pathname | filename | agr list | argv[] | environ | envp[] |
+----------+----------+----------+----------+--------+---------+--------+
|  execl   |    *     |          |     *    |        |    *    |        |
+----------+----------+----------+----------+--------+---------+--------+
|  execlp  |          |    *     |     *    |        |    *    |        |
+----------+----------+----------+----------+--------+---------+--------+
|  execle  |    *     |          |     *    |        |         |   *    |
+----------+----------+----------+----------+--------+---------+--------+
|  execv   |    *     |          |          |    *   |    *    |        |
+----------+----------+----------+----------+--------+---------+--------+
|  execvp  |          |    *     |          |    *   |    *    |        |
+----------+----------+----------+----------+--------+---------+--------+
|  execve  |    *     |          |          |    *   |         |   *    |
+----------+----------+----------+----------+--------+---------+--------+
|  letter  |          |    p     |     l    |    v   |         |   e    |
+----------+----------+----------+----------+--------+---------+--------+

所以在你的情况下execvp需要文件名,argv(v)和environ(e)。然后它尝试通过将filename(在你的情况下cat)附加到每个路径组件中来“猜测”路径名(又名完整路径),PATH直到找到带有可执行文件的路径filename

W. Richard Stevens 和 Stephen A. Rago aka APUE2的 UNIX 环境中的高级编程(第 2 版)exec中可以找到更多关于 . 如果您对 UNIX 内部结构感兴趣,您可能应该阅读它。

于 2013-01-13T23:10:17.873 回答
2

“ls”不仅仅是一个命令,它实际上是一个程序(大多数命令都是)。当你像这样运行 execvp 时,它会破坏你的整个程序,它的内存,它的堆栈,它的堆等等......从概念上“清除它”并将它交给“ls”,这样它就可以将它用于自己的堆栈,堆等

简而言之,execvp 将破坏您的程序,并用另一个程序替换它,在本例中为“ls”。

于 2013-01-13T07:21:52.257 回答
1

我的直觉是我必须阅读文件并可能解析它以将参数存储在 arg 中。不过我想确定一下。

你的直觉在很大程度上是正确的。cat您作为示例使用的实用程序有两个单独的代码路径:

  • 如果有指定为参数的文件名,它将依次打开并读取每个文件名。
  • 如果没有指定文件名,它将从标准输入中读取。

此行为是在cat实用程序中专门实现的——它没有在任何较低级别实现。特别是,它绝对不是exec系统调用的一部分。系统exec调用根本不“查看”参数;他们只是将它们直接传递给 中的新流程argv,然后该流程会以它认为合适的方式处理它们。

于 2013-01-13T08:05:07.327 回答