execvp如何运行命令?

时间:2013-01-13 07:03:22

标签: c file unix

我知道execvp可用于执行简单命令,如下所示:

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

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

具体来说,有一个特别需要输入的命令,例如猫。如果我有一个文本文件text.txt,其中包含cat所需的文件名,并且我将stdin重定向到文件的文件流,那么execle("cat","cat",NULL)execvp("cat", arg)的输出(显然是arg存储的位置{ {1}}和"cat")会导致控制台中的输出为NULL吗?我的直觉是我必须读取文件并可能解析它以将参数存储在arg中。但是我想确定一下。

提前致谢!

4 个答案:

答案 0 :(得分:19)

以下是execvp来电中发生的事情:

  1. 如果适用,您的libc实现会在PATH中搜索要执行的文件。类UNIX系统中的大多数(如果不是全部)命令都是可执行文件。如果不是,会发生什么?试试吧。看看how glibc does it
  2. 通常,如果找到可执行文件,则会调用execve。部分execve可以在libc中实现,也可以是系统调用(如在Linux中)。
  3. Linux通过为其分配内存,打开它,调度执行,初始化内存结构,从提供的参数设置其参数和环境到execvp调用来准备程序,找到适合加载的处理程序二进制文件,并将当前任务(execvp调用者)设置为不执行。您可以找到其实施here
  4. 上述所有步骤均符合POSIX设定的要求,相关manual pages中对此进行了描述。

答案 1 :(得分:12)

关于你的问题:

  

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

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

λ ~/ type echo
echo is a shell builtin

(包含说明的内置列表的完整列表可在您的shell的man页面中找到,例如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的特殊可执行文件(在非常简单的解释中)在通过stdin传递的参数列表上执行给定程序。有关更多信息,请参阅man xargs


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

           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 )和环境( e )。 然后它尝试通过将filename(在您的情况下为cat)附加到PATH中的每个路径组件来“猜测”路径名(也称为完整路径),直到找到具有可执行文件{{1}的路径}。

有关filename引擎盖下的最新信息(包括继承内容)的更多信息可以在Advanced Programming in the UNIX Environment (2nd Edition) by W. Richard Stevens and Stephen A. Rago APUE2中找到。
如果您对UNIX内部感兴趣,您应该阅读它。

答案 2 :(得分:2)

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

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

答案 3 :(得分:1)

  

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

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

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

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