pax*_*blo 353
使用fork和exec体现UNIX的精神,它提供了一种非常简单的方法来启动新进程.
该fork调用基本上复制了当前进程,几乎在所有方面都相同(并非所有内容都被复制,例如,某些实现中的资源限制,但想法是尽可能创建尽可能接近的副本).
新进程(子进程)获取不同的进程ID(PID),并将旧进程(父进程)的PID作为其父PID(PPID).因为这两个进程现在运行完全相同的代码,所以它们可以通过返回代码fork- 孩子得到0 来判断哪个进程,父进程获取子进程的PID.当然,这就是假设fork调用有效 - 如果没有,则不会创建子节点并且父节点获取错误代码.
该exec调用是一种基本上用新程序替换整个当前进程的方法.它将程序加载到当前进程空间并从入口点运行它.
因此,fork与exec通常的顺序来获取所有正在运行的当前进程的孩子一个新的程序.每当你试图运行像findshell 这样的程序时,shell通常会这样做,然后子程序将find程序加载到内存中,设置所有命令行参数,标准I/O等等.
但它们不需要一起使用.例如,如果程序包含父代码和子代码,那么程序fork本身是完全可以接受的exec(你需要小心你做什么,每个实现可能都有限制).这对于守护进程使用了很多(现在仍然如此),它们只是监听TCP端口和fork自己的副本来处理特定请求,同时父进程回去监听.
同样地,知道它们已经完成并且只想运行另一个程序的程序不需要fork,exec然后wait对于孩子.他们可以直接将孩子加载到他们的流程空间.
某些UNIX实现已经过优化fork,它们使用了所谓的copy-on-write.这是一个延迟复制进程空间的技巧,fork直到程序尝试更改该空间中的某些内容.这对于那些仅使用fork而不是exec因为它们不必复制整个进程空间的程序非常有用.
如果exec 被称为以下fork(这是什么会发生大多),导致进程空间写,然后将其复制子进程.
请注意,是全家exec电话(execl,execle,execve等),但exec在上下文中这里是指任何人.
下图说明了使用shell通过命令列出目录的典型fork/exec操作:bashls
+--------+
| pid=7 |
| ppid=4 |
| bash |
+--------+
|
| calls fork
V
+--------+ +--------+
| pid=7 | forks | pid=22 |
| ppid=4 | ----------> | ppid=7 |
| bash | | bash |
+--------+ +--------+
| |
| waits for pid 22 | calls exec to run ls
| V
| +--------+
| | pid=22 |
| | ppid=7 |
| | ls |
V +--------+
+--------+ |
| pid=7 | | exits
| ppid=4 | <---------------+
| bash |
+--------+
|
| continues
V
Run Code Online (Sandbox Code Playgroud)
Dou*_* T. 52
fork()将当前进程拆分为两个进程.换句话说,你很好的线性易思考程序突然变成了运行一段代码的两个独立程序:
int pid = fork();
if (pid == 0)
{
printf("I'm the child");
}
else
{
printf("I'm the parent, my child is %i", pid);
// here we can kill the child, but that's not very parently of us
}
Run Code Online (Sandbox Code Playgroud)
这可能会让你大吃一惊.现在你有一段代码,两个进程执行相同的状态.子进程继承了刚刚创建它的进程的所有代码和内存,包括从fork()刚刚停止的调用开始.唯一的区别是fork()返回代码告诉您您是父母还是孩子.如果您是父级,则返回值是子级的ID.
exec更容易掌握,您只需告诉exec使用目标可执行文件执行进程,并且您没有两个进程运行相同的代码或继承相同的状态.就像@Steve Hawkins所说,exec可以在你fork执行当前进程后使用目标可执行文件.
Mic*_*urr 31
我认为Marc Rochkind的"高级Unix编程"中的一些概念有助于理解fork()/ 的不同角色exec(),特别是对于习惯于Windows CreateProcess()模型的人:
一个方案是被保存在磁盘上的常规文件的指令和数据的集合.(来自1.1.2程序,进程和线程)
.
为了运行程序,首先要求内核创建一个新进程,该进程是程序执行的环境.(也来自1.1.2程序,进程和线程)
.
如果不完全理解进程和程序之间的区别,就无法理解exec或fork系统调用.如果这些条款对您而言是新的,您可能需要返回并查看第1.1.2节.如果您现在准备好了,我们将用一句话概括区别:流程是一个执行环境,包括指令,用户数据和系统数据段,以及在运行时获取的许多其他资源程序是包含指令和数据的文件,用于初始化进程的指令和用户数据段.(来自5.3
exec系统调用)
一旦理解了程序和进程之间的区别,就可以将行为fork()和exec()函数概括为:
fork() 创建当前进程的副本exec() 用另一个程序替换当前进程中的程序(这实际上是一个简化的'for dummies '版本的paxdiablo更详细的答案)
小智 29
Fork创建一个调用进程的副本.一般遵循结构

int cpid = fork( );
if (cpid = = 0)
{
//child code
exit(0);
}
//parent code
wait(cpid);
// end
Run Code Online (Sandbox Code Playgroud)
(对于子进程文本(代码),数据,堆栈与调用进程相同)子进程在if块中执行代码.
EXEC用新进程的代码,数据和堆栈替换当前进程.一般遵循结构

int cpid = fork( );
if (cpid = = 0)
{
//child code
exec(foo);
exit(0);
}
//parent code
wait(cpid);
// end
Run Code Online (Sandbox Code Playgroud)
(在exec调用unix内核之后清除子进程文本,数据,堆栈并填充与foo进程相关的文本/数据)因此子进程使用不同的代码(foo的代码{与父对象不同})
它们一起用于创建新的子进程.首先,调用fork创建当前进程的副本(子进程).然后,exec从子进程内调用"替换"父进程的副本和新进程.
这个过程是这样的:
child = fork(); //Fork returns a PID for the parent process, or 0 for the child, or -1 for Fail
if (child < 0) {
std::cout << "Failed to fork GUI process...Exiting" << std::endl;
exit (-1);
} else if (child == 0) { // This is the Child Process
// Call one of the "exec" functions to create the child process
execvp (argv[0], const_cast<char**>(argv));
} else { // This is the Parent Process
//Continue executing parent process
}
Run Code Online (Sandbox Code Playgroud)
fork()和之间的主要区别exec()在于,
系统fork()调用创建当前运行程序的克隆。原始程序在 fork() 函数调用后继续执行下一行代码。克隆也会从下一行代码开始执行。\n看看我从http://timmurphy.org/2014/04/26/using-fork-in-cc-a-minimum-working-example/获得的以下代码
#include <stdio.h>\n#include <unistd.h>\nint main(int argc, char **argv)\n{\n printf("--beginning of program\\n");\n int counter = 0;\n pid_t pid = fork();\n if (pid == 0)\n {\n // child process\n int i = 0;\n for (; i < 5; ++i)\n {\n printf("child process: counter=%d\\n", ++counter);\n }\n }\n else if (pid > 0)\n {\n // parent process\n int j = 0;\n for (; j < 5; ++j)\n {\n printf("parent process: counter=%d\\n", ++counter);\n }\n }\n else\n {\n // fork failed\n printf("fork() failed!\\n");\n return 1;\n }\n printf("--end of program--\\n");\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n该程序在 ing 之前声明了一个计数器变量,并将其设置为零fork()。在 fork 调用之后,我们有两个并行运行的进程,两个进程都递增自己的计数器版本。每个进程都会运行到完成并退出。由于进程是并行运行的,因此我们无法知道哪个进程将首先完成。运行该程序将打印类似于下图所示的内容,但每次运行的结果可能会有所不同。
--beginning of program\nparent process: counter=1\nparent process: counter=2\nparent process: counter=3\nchild process: counter=1\nparent process: counter=4\nchild process: counter=2\nparent process: counter=5\nchild process: counter=3\n--end of program--\nchild process: counter=4\nchild process: counter=5\n--end of program--\nRun Code Online (Sandbox Code Playgroud)\n\n系统调用系列exec()将进程当前正在执行的代码替换为另一段代码。该进程保留其 PID,但它成为一个新程序。例如,考虑以下代码:
#include <stdio.h> \n#include <unistd.h> \nmain() {\n char program[80],*args[3];\n int i; \nprintf("Ready to exec()...\\n"); \nstrcpy(program,"date"); \nargs[0]="date"; \nargs[1]="-u"; \nargs[2]=NULL; \ni=execvp(program,args); \nprintf("i=%d ... did it work?\\n",i); \n} \nRun Code Online (Sandbox Code Playgroud)\n\n该程序调用该execvp()函数将其代码替换为日期程序。如果代码存储在名为 exec1.c 的文件中,则执行它会产生以下输出:
Ready to exec()... \nTue Jul 15 20:17:53 UTC 2008 \nRun Code Online (Sandbox Code Playgroud)\n\n程序将行 \xe2\x80\x95Ready 输出到 exec() 。。。\xe2\x80\x96 并在调用 execvp() 函数后,将其代码替换为日期程序。请注意行 \xe2\x80\x95 。。。did it work\xe2\x80\x96 不显示,因为此时代码已被替换。相反,我们看到执行 \xe2\x80\x95date -u.\xe2\x80\x96 的输出
\n