system
和exec
命令族的区别是什么?尤其是我想知道它们中哪一个会创建子进程来工作?
system()
调用 sh
处理你的命令行,因此您可以获得通配符扩展等功能。而 exec()
及其相关函数替换当前进程映像为一个新的进程映像。
使用 system()
,您的程序会继续运行,并返回一些关于所调用外部命令的状态信息。使用 exec()
,则会将您的进程消除掉。
总的来说,你可以把 system()
视为更高层次的接口。您可以使用 fork()
、exec()
和 wait()
的某种组合来复制其功能。
回答您的最后一个问题,system()
会创建一个子进程,而 exec()
系列不会。您需要使用 fork()
来实现。
当exec函数成功时,它会替换当前正在运行的进程镜像,不会创建子进程(除非您之前使用了fork()
)。而system()函数则会创建一个子进程,并在所提供的命令执行完毕或出现错误时返回。
system()
会在一个新的子进程中执行提供的命令。而 exec()
则会用你指定的新可执行文件来替换当前进程。如果你想使用 exec
来生成一个子进程,你需要先使用 fork()
复制一份当前进程。
fork(2)
,直接调用内核的系统调用execve(2)
,直接调用内核的系统调用,通常称为exec
wait(2)
,直接调用内核的系统调用system(3)
,一个库函数 $ man 2 fork execve wait
$ man 3 system
system()函数将调用您系统的默认命令shell,该shell将执行作为参数传递的命令字符串,该字符串本身可能会创建更多进程,这取决于命令和系统。无论如何,至少会创建一个命令shell进程。
使用system()函数可以调用任何命令,而使用exec()函数只能调用可执行文件。Shell脚本和批处理文件必须由命令shell执行。
基本上,它们用于不同的目的,完全不同。此外,exec()函数会替换调用进程,并且不会返回。更有用的比较应该是system()和spawn()之间的比较。虽然system()函数可能更简单易用,但它返回一个值,告诉您是否调用了命令shell,并且不会告诉您命令本身的成功与否。使用spawn()函数,可以获取进程的退出代码;按照惯例,非零用于指示错误条件。与exec()函数一样,spawn()函数必须调用可执行文件,而不能调用shell脚本或内置命令。
int system(const char *cmdstring);
例如:system("date > file");
一般情况下,system 函数通过调用 fork、exec 和 waitpid 实现,返回值有三种类型。
fork 函数用于创建一个新进程(子进程),然后通过调用其中的一个 exec 函数来启动另一个程序。当一个进程调用其中的一个 exec 函数时,该进程将被全新的程序完全替换,而新程序则在其 main 函数开始执行。进程 ID 在 exec 中不会改变,因为并未创建新进程。exec 只是将当前进程的文本、数据、堆和栈段替换为磁盘上的全新程序。
共有六种不同的 exec 函数:
int execl(const char *pathname, const char *arg0, ... /* (char *)0 */ );
int execv(const char *pathname, char *const argv []);
int execle(const char *pathname, const char *arg0, .../* (char *)0, char *const envp[] */ );
int execve(const char *pathname, char *const argv[], char *const envp []);
int execlp(const char *filename, const char *arg0,... /* (char *)0 */ );
int execvp(const char *filename, char *const argv []);
JonSpencer的回答很好,除了child_status必须是整数(不是整数指针),并且必须通过引用传递给等待函数。
因此,代码基本相同,只需更改这些内容:
#include <unistd.h>
#include <sys/wait.h>
#define NUMARGS 2
int main (int argc, char *argv[])
{
pid_t child_pid, wait_pid;
int child_status;
char * exec_path = "/path/to/executable";
char * child_args[NUMARGS] = {0,0};
child_pid = fork();
if (0 == child_pid)
{ // In child process
...
int child_ret_code = execv(exec_path, child_args); //or whichever flavor of exec() that floats your boat
... // if child_ret_code = -1, process execv() error return
}
else if (-1 == child_pid)
{
... //process error return from fork
}
else if (0 < child_pid)
{ // Parent process
wait_pid = wait(&child_status);
if (-1 == wait_pid)
{
... //Process error return from wait()
}
else
{ // Good fork/exec/wait
if (WIFEXITED(child_status)) // Child exited normally and hopefully returned exit code
{
int child_ret_code = WEXITSTATUS(child_status);
... // Continue on as you would after call to system(3)
// except now you have the return code you needed
}
}
}
}
请指出,我还没有足够的声誉来评论Jon的帖子,所以我编辑了它。一些人拒绝了这个修改,并要求我回答问题而不是编辑它,但在这种情况下,仅仅更正一个小错误就可以编辑现有的代码比编写完整的复制/粘贴/修改答案要简单、实用和清晰得多。
无论如何,感谢JonSpencer的回答,对我非常有用!
exec(2)
和system(3)
时需要注意一些重要的区别。 system()
返回给调用者,而exec()
将现有代码替换为新映像。这已经在上面解释过了。system()
确实提供了一个返回代码,但该返回代码只能用于检测错误条件,不能用于恢复返回代码。#include <unistd.h>
#include <sys/wait.h>
#define NUMARGS 2
int main (int argc, char *argv[])
{
pid_t child_pid, wait_pid;
int * child_status;
char * exec_path = "/path/to/executable";
char * child_args[NUMARGS] = {0,0};
child_pid = fork();
if (0 == child_pid)
{ // In child process
...
int child_ret_code = execv(exec_path, child_args); //or whichever flavor of exec() that floats your boat
... // if child_ret_code = -1, process execv() error return
}
else if (-1 == child_pid)
{
... //process error return from fork
}
else if (0 < child_pid)
{ // Parent process
wait_pid = wait(child_status);
if (-1 == wait_pid)
{
... //Process error return from wait()
}
else
{ // Good fork/exec/wait
if (WIFEXITED(child_status)) // Child exited normally and hopefully returned exit code
{
int child_ret_code = WEXITSTATUS(child_status);
... // Continue on as you would after call to system(3)
// except now you have the return code you needed
}
}
}
}
这个序列还有其他微妙之处,可以通过仔细阅读相关的man页面来确定,但是在没有信号、多个子进程等情况下,该代码将正常工作。此外,内联声明可能会限制变量的范围,但包含在内以允许该代码用作模板(您可以使用不同的编码风格:-)。
exec() 函数用正在执行的函数进程图像替换当前运行的进程。只有可执行文件才能使用此函数。
system() 函数隐式地分叉出一个新进程来服务请求,并返回它通过最初分叉的子进程获得的值。它使用系统默认的 shell 来执行操作。
System()函数会创建子进程并调用其他子shell,而exec()不会创建子进程。下面的示例将清楚地展示它们之间的区别。
一些代码...
exec('ls -l')
echo "1 2 3" //这不会在bash中执行(因为exec命令使用相同的shell)
一些代码...
system(ls -l) echo "1 2 3" //这将在完成System子进程后被执行,因为它们与父PID不同。
system
调用是否也会新建一个 shell 来执行给定的命令,还是在同一 shell 中执行命令? - Krishna Ozasystem()
的程序本身就是一个shell,否则不存在“同一shell”。我不确定我是否理解了。我的文档在这里说:“system()
函数将参数命令交给命令解释器sh(1)
。” - Carl Norumsystem
POSIX手册中引用的内容:*system()
函数的行为就像使用fork()
创建了一个子进程,并且该子进程使用以下方式使用execl()
调用了sh实用程序:execl(<shell path>, "sh", "-c", command, (char *)0);
*。 - patryk.beza