Linux 中“ system”和“ exec”的区别?

systemexec系列命令的区别是什么?特别是我想知道他们中的哪一个创建子过程来工作?

90316 次浏览

Exec 函数在成功时替换当前正在运行的进程映像,不会创建子进程(除非您以前使用 fork()自己创建子进程)。System ()函数分叉子进程,并在提供的命令执行完毕或出现错误时返回。

system()调用到 sh来处理命令行,这样就可以获得通配符扩展,等等。exec()和它的朋友们用一个新的过程图像代替了当前的过程图像。

使用 system(),程序将继续运行,并且您将获得有关所调用的外部命令的一些状态。使用 exec(),您的过程将被消除。

通常,我想您可以将 system()看作是一个更高级的接口。您可以使用一些组合 fork()exec()wait()自己复制它的功能。

要回答最后一个问题,system()会创建一个子进程,而 exec()家族不会。这需要使用 fork()

system()将在它生成的子进程中执行所提供的命令。exec()将用您指定的新可执行文件的调用替换当前进程。如果希望使用 exec产生子进程,则必须事先使用 fork()生成进程。

System ()将调用您的系统默认命令 shell,该命令将执行作为参数传递的命令字符串,该命令字符串本身可能创建也可能不创建进一步的进程,这将依赖于命令和系统。无论采用哪种方式,都将至少创建一个命令 shell 进程。

使用 system ()可以调用任何命令,而使用 exec ()只能调用可执行文件。Shell 脚本和批处理文件必须由命令 Shell 执行。

基本上,它们用于不同的目的是完全不同的。此外,exec ()替换了调用进程,并且不返回。一个更有用的比较是 system ()和 spawn ()之间的比较。虽然 system 可能更容易调用,但它返回一个值,告诉您是否调用了命令 shell,而不告诉您有关命令本身成功的任何信息。使用 spawn ()可以获得进程的退出代码; 按照惯例,非零用于指示错误条件。与 exec () spawn ()一样,spawn ()必须调用可执行文件,而不是 shell 脚本或内置命令。

创建进程:

  • fork(2),直接对内核的系统调用

执行程序,替换当前映像:

  • execve(2),一个直接对内核的系统调用,通常只称为 exec

等待子进程完成:

  • wait(2),直接对内核的系统调用

在子进程的 shell 中运行程序并等待它结束:

  • system(3),一个库函数

为了得到以上所有的 手册:

   $ man 2 fork execve wait
$ man 3 system

Exec ()将当前正在运行的进程替换为正在执行的函数的进程映像。.只有可执行文件可以使用。

System ()隐式地分叉出一个新进程来为请求提供服务,并返回它通过最初分叉的子进程获得的值。它使用系统的默认 shell 来执行操作。

System ()将创建子进程并调用另一个子 shell,而 exec ()将不创建子进程。给定的例子会清楚的区别。

一些密码。

Exec (‘ ls-l’)

Echo“123”//这将不在 bash 中执行(因为 exec 命令使用相同的 shell)

一些密码。

系统(ls-l) Echo“123”//这将在完成 System 子进程后执行,因为它们与父 PID 不同。

System ()使用 shell 调用所需的程序或内置命令,这是一种低效的方法,因为 shell 在程序启动之前就已经启动了。

在 exec 系列系统调用的情况下,将创建一个全新的映像,也就是说,它们将当前进程替换为由路径或文件或您提到的任何参数指定的新进程。

需要记住的是,当使用 exec 系列系统调用时,在启动新程序之后,原始程序将不再运行。

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
}
}
}
}

通过仔细阅读相关的手册页,可以确定这个序列还有其他的微妙之处,但是在没有信号、多个子进程等情况下,这个代码可以很好地工作。另外,内联声明可能会限制变量的范围,但是包含这些声明是为了允许将这些代码用作可以工作的模板(您可以使用不同的编码风格: ——)。

一般来说,“ system”效率很低,除非你有一个很小的代码,否则你不应该使用它。如果您需要在您的流程中执行多个程序,那么您最好使用 fork & exec,尽管这会使它变得更加复杂。 下面是它们之间的一些不同之处:

1-“ system”命令创建一个 shell 副本来执行程序。每次调用系统时,都会创建一个 shell 副本。因此,当您有很多程序要在进程中执行时,不要使用它。

2-具体来说,如果你想执行系统函数,比如“ mv”,“ mkdir”,最好使用例程,比如 mkdir () ,unlink ()或 delete () ,而不是通过“ system (“ rm... .”)或 system (“ mkdir... .”)”来执行它们。

由于系统调用 shell 来执行所需的程序,您可能会遇到一些用户权限问题。例如,有人可能会破解您的代码并执行其他代码,而不是您打算通过系统命令执行的程序。

欲了解更多信息,请阅读本书第11章: “ UNIX 系统编程”David Curry 著。

JonSpencer 的回答很好,除了 child _ status 必须是一个 int (没有指向 int 的指针) ,并且必须通过引用传递给 wait 函数。

所以,代码基本上是一样的,只是改变了一些东西:

#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 的回答,这对我真的很有用!


int system(const char *cmdstring);

例句: system("date > file");


一般来说,系统是通过调用 分叉,执行,等待来实现的,返回值有三种类型。

  • 如果 fork 失败,或者 waitpid 返回一个 EINTR 以外的错误,系统返回-1并设置 errno 来指示错误。
  • 如果 exec 失败,意味着不能执行 shell,返回值就好像 shell 已经执行了一样 出口(127)。
  • 否则,所有三个函数ー fork、 exec 和 waitpid ー都会成功,并从系统返回值 是 shell 的终止状态,格式为 waitpid 指定。

叉子函数将创建一个新进程(子进程) ,然后 通过调用一个 执行官函数导致另一个程序被执行 执行 exec 函数时,该进程被新程序完全替换,新程序开始执行 它的主要功能。由于没有创建新的进程,因此进程 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 []);