第五章-进程API

知识点

fork系统调用

1、子进程不会从 main()函数开始执行,而是直接从 fork()系统调用返回。

2、子进程拥有自己的地址空间(即拥有自己的私有内存)、寄存器、程序计数器等

3、父进程获得的返回值是新创建子进程的 PID,而子进程获得的返回值是 0

4、子进程和父进程的运行顺序取决于CPU调度顺序

代码实现

 #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 failed\n");
 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()才返回父进程。

代码实现

#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 failed\n");
 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;
 }

运行结果

prompt> ./p2
hello world (pid:29266)
hello, I am child (pid:29267)
hello, I am parent of 29267 (wc:29267) (pid:29266)
prompt>
exec系统调用

这个系统调用可以让子进程执行与父进程不同的程序。例如,在p2.c 中调用 fork(),这只是在你想运行相同程序的拷贝谁有用。但是,我们常常想运行不同的程序,exec()正好做这样的事

这个例子中,子进程调用 execvp()来运行字符计数程序 wc。

exec()会从可执行程序中加载代码和静态数据,并用它覆写自己代码段(以及静态数据),堆、栈及其他内存空间也会被重新初始化。然后操作系统就执行该程序,将参数通过 argv 传递给该进程。因此,它并没有创建新进程,而是直接将当前运行的程序(以前的 p3)替换为不同的运行程序(wc)。子进程执行 exec()之后,几乎就像p3.c 从未运行过一样。对 exec()的成功调用永远不会返回

prompt> ./p3
hello world (pid:29383)
hello, I am child (pid:29384)
29 107 1030 p3.c
hello, I am parent of 29384 (wc:29384) (pid:29383)
prompt>
 #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 failed\n");
 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;
 }
why splits fork() and 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 的输出结果就被发送到该文件,而不是打印在屏幕上。

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐