我知道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中。但是我想确定一下。
提前致谢!
答案 0 :(得分:19)
以下是execvp
来电中发生的事情:
PATH
中搜索要执行的文件。类UNIX系统中的大多数(如果不是全部)命令都是可执行文件。如果不是,会发生什么?试试吧。看看how glibc does it。execve
。部分execve
可以在libc中实现,也可以是系统调用(如在Linux中)。execvp
调用来准备程序,找到适合加载的处理程序二进制文件,并将当前任务(execvp
调用者)设置为不执行。您可以找到其实施here。上述所有步骤均符合POSIX设定的要求,相关manual pages中对此进行了描述。
答案 1 :(得分:12)
关于你的问题:
在手册页中,它显示
execvp
替换过程映像的图像 用新的。但是在这里我运行的命令不是 可执行文件。
很久以前shell非常有限,几乎所有的UNIX命令都是独立的可执行文件。现在,主要是出于速度目的,UNIX命令的一些子集在shell本身内部实现,这些命令称为builtins
。您可以通过type
命令检查shell中实现的任何命令是否为内置命令:
λ ~/ type echo
echo is a shell builtin
(包含说明的内置列表的完整列表可在您的shell的man
页面中找到,例如man bash-builtins
或man 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
中的新流程,然后该流程会处理它们,但它认为合适。