system
和exec
家庭命令有什么区别?特别是我想知道其中哪个创建了子流程?
Answers:
system()
调用sh
以处理您的命令行,因此您可以进行通配符扩展等。 exec()
它的朋友可以用新的过程映像替换当前过程映像。
使用system()
,您的程序将继续运行,您将获得有关所调用外部命令的某些状态。使用exec()
,您的过程将消失。
通常,我想您可以将其system()
视为更高级别的界面。你可以使用一些组合复制它的功能你自己fork()
,exec()
和wait()
。
要回答您的最后一个问题,将system()
导致创建子进程,而exec()
家庭则不会。您将需要使用fork()
它。
system()
本身就是外壳。我不确定是否要遵循。我在这里的文档说:“该system()
函数将自变量命令交给命令解释器sh(1)
。”
system
POSIX手册:该system()
功能应行为,如果一个子进程所使用的创作fork()
,和子进程使用调用的SH工具execl()
如下:execl(<shell path>, "sh", "-c", command, (char *)0);
。
成功时,exec函数将替换当前正在运行的过程映像,不会创建任何子级(除非您以前使用来进行此操作fork()
)。system()函数确实派生一个子进程,并在提供的命令完成执行或发生错误时返回。
system()将调用系统的默认命令外壳,该外壳将执行作为参数传递的命令字符串,它本身可能会或可能不会创建进一步的进程,这将取决于命令和系统。无论哪种方式,都将至少创建一个命令外壳进程。
使用system()可以调用任何命令,而使用exec()则只能调用可执行文件。Shell脚本和批处理文件必须由命令Shell执行。
基本上,它们用于不同目的完全不同。而且exec()替换了调用过程,并且不返回。一个更有用的比较是在system()和spawn()之间。虽然system可能更易于调用,但它会返回一个值,该值告诉您是否已调用命令外壳程序,并且不告诉您命令本身是否成功。使用spawn()可以获取进程的退出代码。按照惯例,非零用于指示错误情况。像exec()一样,spawn()必须调用可执行文件,而不是Shell脚本或内置命令。
int system(const char *cmdstring);
例如: system("date > file");
通常,系统是通过调用fork,exec和waitpid来实现的,返回值有三种类型。
该叉的功能是创建一个新的过程(子),其然后导致另一个程序通过调用的一个被执行的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 []);
之间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的指针)并且必须通过引用传递给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的帖子,所以我对其进行了编辑。有些人拒绝了该版本,要求我回答问题而不是对其进行编辑,但是我认为在这种情况下,它更加简单,实用并明确地编辑一个现有的代码仅能纠正一个小错误,而不是编写完整的复制/粘贴/修改答案。
通常,“系统”的效率很低,除非代码很小,否则不应使用它。如果您需要在流程中执行多个程序,则最好使fork&exec更为复杂。以下是它们之间的差异列表:
1-“ system”命令创建一个Shell副本来执行您的程序。每次调用系统时,都会创建一个shell副本。因此,当您在进程中要执行许多程序时,请不要使用它。
2-具体来说,如果要执行“ mv”,“ mkdir”之类的系统功能,最好使用mkdir(),unlink()或remove()之类的例程,而不要通过“ system(”)执行它们rm ....“)或system(” mkdir ....“)”。
3-由于系统调用Shell来执行所需的程序,因此您可能会遇到一些用户权限问题。例如,某人可能会破解您的代码并执行其他操作,而不是您打算通过系统命令执行的程序。
有关更多信息,您可以阅读本书的第11章:David Curry的“ UNIX系统编程”。
system
调用是否也产生一个新的Shell来执行给定的命令,或者在同一Shell中执行该命令。