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

时间:2009-11-08 18:29:48

标签: c linux exec fork

systemexec系列命令之间有什么区别?特别是我想知道哪一个创建子进程工作?

12 个答案:

答案 0 :(得分:86)

system()调用sh来处理您的命令行,因此您可以进行通配符扩展等。exec()及其朋友用新的过程映像替换当前过程映像。

使用system(),您的程序将继续运行,并返回有关您调用的外部命令的某些状态。使用exec(),您的流程即被删除。

一般来说,我猜您可以将system()视为更高级别的界面。您可以使用某种组合fork()exec()wait()自行复制其功能。

要回答最后一个问题,system()会导致创建子进程,而exec()系列则不会。您需要使用fork()

答案 1 :(得分:19)

exec函数在成功时替换当前正在运行的过程映像,不创建子项(除非您以前使用fork()执行此操作)。 system()函数会对子进程执行派生,并在提供的命令执行完毕或发生错误时返回。

答案 2 :(得分:7)

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

答案 3 :(得分:6)

创建流程:

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

要执行程序,请替换当前图像:

  • execve(2),系统直接调用内核,通常只调用exec

等待子进程完成:

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

在子进程的shell中运行程序并等待它完成:

  • system(3),图书馆功能

要获得上述所有内容的man pages

   $ man 2 fork execve wait
   $ man 3 system

答案 4 :(得分:2)

system()将调用你的系统默认命令shell,它将执行作为参数传递的命令字符串,它本身可能会或可能不会创建进一步的进程,这取决于命令和系统。无论哪种方式,至少会创建一个命令shell进程。

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

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

答案 5 :(得分:2)

int system(const char *cmdstring);

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

一般来说,系统是通过调用 fork,exec和waitpid 来实现的,有三种类型的返回值。

  • 如果fork失败或者waitpid返回EINTR以外的错误,则系统返回-1并设置errno 表示错误。
  • 如果exec失败,暗示无法执行shell,则返回值就像shell已执行一样 出口(127)。
  • 否则,所有三个函数 - fork,exec和waitpid-success,以及系统的返回值 是shell的终止状态,采用为waitpid指定的格式。

fork 函数用于创建一个新进程(子进程) 通过调用 exec 函数之一来执行另一个程序。当一个进程调用其中一个 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 []);

答案 6 :(得分:1)

exec()将当前正在运行的进程替换为正在执行的函数的进程映像。只能使用此函数调用可执行文件。

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

答案 7 :(得分:1)

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

这个序列还有其他微妙之处,可以通过仔细阅读相关的手册页来确定,但是这个代码在没有信号,多个子进程等的情况下工作正常。此外,内联声明可能会限制变量的范围,但包括在内以允许此代码用作有效的模板(您可以使用不同的编码样式: - )。

答案 8 :(得分:0)

System()将创建子进程并调用另一个子shell,而exec()将不会创建子进程.Given示例将清除差异。

一些代码......

exec('ls -l')

echo“1 2 3”//这不会在bash中执行(因为exec命令使用相同的shell)

一些代码......

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

答案 9 :(得分:0)

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

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

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

答案 10 :(得分:0)

一般来说,&#34; system&#34;是如此低效,你不应该使用它,除非你有一个小代码。如果你需要在你的过程中执行几个程序,你最好使用fork&amp; exec,尽管你把它变得更复杂。  以下是它们之间的差异列表:

1-&#34; system&#34;命令创建shell的副本以执行您的程序。每次调用系统时,都会创建一个shell副本。因此,当您在流程中执行大量程序时,请不要使用它。

2-具体来说,如果你想执行系统功能,如&#34; mv&#34;,&#34; mkdir&#34;,最好使用例如mkdir(),unlink()等例程或者删除()而不是通过&#34;系统(&#34; rm ....&#34;)或系统(&#34; mkdir ....&#34;)&#34;执行它们。

3-由于系统调用shell来执行所需的程序,因此可能存在一些用户权限问题。例如,某人可能会破解您的代码并执行其他操作而不是您希望通过系统命令执行的程序。

有关详细信息,请阅读本书第11章: &#34; UNIX系统编程&#34;作者:David Curry。

答案 11 :(得分:0)

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