【发布时间】:2010-12-14 10:09:12
【问题描述】:
system 和exec 系列命令有什么区别?特别是我想知道其中哪一个创建子进程来工作?
【问题讨论】:
system 和exec 系列命令有什么区别?特别是我想知道其中哪一个创建子进程来工作?
【问题讨论】:
system() 调用sh 来处理您的命令行,因此您可以获得通配符扩展等。exec() 及其朋友用新的进程映像替换当前进程映像。
使用system(),您的程序会继续运行,并且您会返回有关您调用的外部命令的一些状态。使用exec(),您的进程将被抹杀。
一般来说,我想您可以将system() 视为更高级别的接口。您可以使用 fork()、exec() 和 wait() 的组合自己复制其功能。
要回答您的最后一个问题,system() 会创建一个子进程,而 exec() 家族则不会。为此,您需要使用 fork()。
【讨论】:
system 调用是否会生成一个新的 shell 来执行给定的命令,或者它在同一个 shell 中执行命令。
system() 的程序本身就是一个外壳。我不确定我是否遵循。我在这里的文档说:“system() 函数将参数命令传递给命令解释器sh(1)。”
system POSIX manual: system() 函数的行为就像使用fork() 创建子进程一样,并且子进程使用execl() 调用 sh 实用程序,如下所示:execl(<shell path>, "sh", "-c", command, (char *)0);.
exec 函数在成功时替换当前正在运行的进程映像,不会创建子进程(除非您之前使用fork() 自己这样做)。 system() 函数会派生一个子进程,并在提供的命令执行完毕或发生错误时返回。
【讨论】:
system() 将在它产生的子进程中执行提供的命令。 exec() 将用您指定的新可执行文件的调用替换当前进程。如果您想使用exec 生成子进程,则必须事先fork() 您的进程。
【讨论】:
创建一个进程:
fork(2),直接对内核的系统调用执行程序,替换当前图像:
execve(2),直接对内核的系统调用,通常就叫exec
等待子进程完成:
wait(2),直接对内核的系统调用在子进程的 shell 中运行程序并等待它完成:
system(3),一个库函数要获得以上所有内容的man pages:
$ man 2 fork execve wait
$ man 3 system
【讨论】:
system() 将调用您的系统默认命令外壳,它将执行作为参数传递的命令字符串,它本身可能会或可能不会创建进一步的进程,这取决于命令和系统。无论哪种方式,都至少会创建一个命令 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实现的,返回值有3种。
fork 功能是创建一个新进程(子进程),然后 通过调用 exec 函数之一来执行另一个程序。当一个进程调用其中一个 exec 函数,该进程完全被新程序替换,新程序开始执行 在它的主要功能。进程 ID 在 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 []);
【讨论】:
exec() 将当前正在运行的进程替换为正在执行的函数的进程映像。使用此函数只能调用可执行文件。
system() 隐式分叉一个新进程来服务请求,并返回它通过它最初分叉的子进程获得的值。它使用系统的默认 shell 来执行操作。
【讨论】:
应该牢记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
}
}
}
}
通过仔细阅读相关手册页可以确定此序列的其他细微之处,但此代码在没有信号、多个子进程等情况下可以正常工作。此外,内联声明可能会限制变量的范围,但包含在内是为了允许将此代码用作有效的模板(您可以使用不同的编码风格:-)。
【讨论】:
JonSpencer 的回答很好,除了 child_status 必须是一个 int(不是指向 int 的指针)并且必须通过引用传递给等待函数。
所以,代码基本上是相同的,只是改变了这几件事:
#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 的回答,这对我来说真的很有用!
【讨论】:
System() 将创建子进程并调用另一个子 shell,而 exec() 不会创建子进程。给定示例将清楚区别。
一些代码...
exec('ls -l')
echo "1 2 3" // 这不会在 bash 中执行(因为 exec 命令使用相同的 shell)
一些代码...
系统 (ls -l) echo "1 2 3" // 这将在完成系统子进程后执行,因为它们与父 PID 不同。
【讨论】:
system() 使用 shell 调用所需的程序或内置命令,这是一种低效的方式,因为在程序启动之前启动了 shell。
在 exec 系列系统调用的情况下,正在创建一个全新的映像,也就是说,它们将当前进程替换为由路径或文件或您提到的任何参数指定的新进程。
要记住的是,当使用 exec 系列系统调用时,新程序启动后,原程序将不再运行。
【讨论】:
一般来说,“系统”的效率很低,除非你的代码很小,否则你不应该使用它。如果你需要在你的进程中执行多个程序,你最好使用 fork&exec ,尽管你让它变得更复杂。 以下是它们之间的差异列表:
1-“system”命令创建一个shell副本来执行你的程序。每次调用系统时,都会创建一个 shell 副本。因此,当您的进程中有很多程序要执行时,请不要使用它。
2- 具体来说,如果要执行“mv”、“mkdir”等系统函数,最好使用mkdir()、unlink()或remove()等例程,而不是通过“ system("rm ....") 或 system("mkdir ....")"。
3- 由于系统调用 shell 来执行您想要的程序,您可能会遇到一些用户权限问题。例如,有人可能会破解您的代码并执行其他内容,而不是您打算通过系统命令执行的程序。
有关更多信息,您可以阅读本书的第 11 章: David Curry 的“UNIX 系统编程”。
【讨论】: