fork,wait和exec
- fork系统调用
- wait系统调用
- exec系统调用
- 为什么要把fork和exec分开
fork系统调用
1、子进程不会从 main()函数开始执行,而是直接从 fork()系统调用返回。
2、子进程拥有自己的地址空间(即拥有自己的私有内存)、寄存器、程序计数器等
3、父进程获得的返回值是新创建子进程的 PID,而子进程获得的返回值是 0
4、子进程和父进程的运行顺序取决于CPU调度顺序
代码实现
代码语言:javascript复制 #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
printf("hello world (pid:%d)n", (int) getpid());
int rc = fork();
if (rc < 0) { // fork failed; exit
fprintf(stderr, "fork failedn");
exit(1);
} else if (rc == 0) { // child (new process)
printf("hello, I am child (pid:%d)n", (int) getpid());
} else { // parent goes down this path (main)
printf("hello, I am parent of %d (pid:%d)n",
rc, (int) getpid());
}
return 0;
}
wait系统调用
父进程调用 wait(),延迟自己的执行,直到子进程执行完毕。当子进程结束,wait()才返回父进程。
代码实现
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
printf("hello world (pid:%d)n", (int) getpid());
int rc = fork();
if (rc < 0) { // fork failed; exit
fprintf(stderr, "fork failedn");
exit(1);
} else if (rc == 0) { // child (new process)
printf("hello, I am child (pid:%d)n", (int) getpid());
} else { // parent goes down this path (main)
int wc = wait(NULL);
printf("hello, I am parent of %d (wc:%d) (pid:%d)n",
rc, wc, (int) getpid());
}
return 0;
}
exec系统调用
这个系统调用可以让子进程执行与父进程不同的程序。例如,在p2.c 中调用 fork(),这只是在你想运行相同程序的拷贝谁有用。但是,我们常常想运行不同的程序,exec()正好做这样的事
这个例子中,子进程调用 execvp()来运行字符计数程序 wc。
exec()会从可执行程序中加载代码和静态数据,并用它覆写自己代码段(以及静态数据),堆、栈及其他内存空间也会被重新初始化。然后操作系统就执行该程序,将参数通过 argv 传递给该进程。因此,它并没有创建新进程,而是直接将当前运行的程序(以前的 p3)替换为不同的运行程序(wc)。子进程执行 exec()之后,几乎就像p3.c 从未运行过一样。对 exec()的成功调用永远不会返回
代码语言:javascript复制 #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
int
main(int argc, char *argv[])
{
printf("hello world (pid:%d)n", (int) getpid());
int rc = fork();
if (rc < 0) { // fork failed; exit
fprintf(stderr, "fork failedn");
exit(1);
} else if (rc == 0) { // child (new process)
printf("hello, I am child (pid:%d)n", (int) getpid());
char *myargs[3];
myargs[0] = strdup("wc"); // program: "wc" (word count)
myargs[1] = strdup("p3.c"); // argument: file to count
myargs[2] = NULL; // marks end of array
execvp(myargs[0], myargs); // runs word count
printf("this shouldn't print out");
} else { // parent goes down this path (main)
int wc = wait(NULL);
printf("hello, I am parent of %d (wc:%d) (pid:%d)n",
rc, wc, (int) getpid());
}
return 0;
}
为什么要把fork和exec分开
在构建UNIX shell 的时候非常有用,因为这给了shell 在fork 之后exec 之前运行代码的机会,这些代码可以在运行新程序前改变环境,实现一些有趣的功能。
例: prompt> wc p3.c > newfile.txt
在上面的例子中,wc 的输出结果被重定向(redirect)到文件newfile.txt 中(通过newfile.txt之前的大于号来指明重我向)。
shell 实现结果重定向的方式也很简单,当完成子进程的创建后,shell在调用exec()之前先关闭了标准输出(standardoutput),打开了文件newfile.txt。
这样,即将运行的程序wc 的输出结果就被发送到该文件,而不是打印在屏幕上。