fork函数总结

简介:

在Unix/Linux中用fork函数创建一个新的进程。进程是由当前已有进程调用fork函数创建,分叉的进程叫子进程,创建者叫父进程。该函数的特点是调用一次,返回两次,一次是在父进程,一次是在子进程。两次返回的区别是子进程的返回值为0,父进程的返回值是新子进程的ID。子进程与父进程继续并发运行。如果父进程继续创建更多的子进程,子进程之间是兄弟关系,同样子进程也可以创建自己的子进程,这样可以建立起定义关系的进程之间的一种层次关系。

  程序包含位于内存的多个组成部分,执行程序的过程将根据需要来访问这些内容,包括文本段(text segment)、数据段(data segments)、栈(stack)和堆(heap)。文本段中存放CPU所执行的命令,数据段存放进程操作的所有数据变量,栈存放自动变量和函数数据,堆存放动态内存分配情况数据。当进程被创建时,子进程收到父进程的数据副本,包括数据空间、堆、栈和进程描述符。

程序1:创建一个子进程,子进程对继承的数据进行修改,然后分别输出父子进程的信息。程序如下:

复制代码
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 #include <unistd.h>
 5 #include <errno.h>
 6 #include <sys/types.h>
 7 
 8 int add(int a,int b);
 9 //全局变量
10 int global = 99;
11 char buf[] = "Input a string: ";
12 
13 int main()
14 {
15     pid_t   pid;
16     int val,ret;
17     char *str;
18     val =49;
19     str = (char*)malloc(100*sizeof(char));
20     memset(str,0,100*sizeof(char));
21     if((pid = fork()) == -1)
22     {
23         perror("fork() error");
24         exit(-1);
25     }
26     if(pid == 0)   //子进程
27     {
28         printf("Child process start exec.\n");
29         global++;
30         val++;
31     }
32     if(pid >0)   //父进程
33     { 
34          sleep(10);   //等待子进程执行
35          printf("Parent process start exec.\n");
36     }
37     printf("pid=%d,ppid=%d,global=%d,val=%d\n",getpid(),getppid(),global,val);
38     write(STDOUT_FILENO,buf,strlen(buf));
39     read(STDIN_FILENO,str,100);
40     write(STDOUT_FILENO,str,strlen(str));
41     ret = add(global,val);
42     printf("global+val=%d\n",ret);
43     exit(0);
44 }
45 
46 int add(int a,int b)
47 {
48     return (a+b);
49 }
复制代码

fork函数执行后程序结构图如下:

子进程与父进程并行执行,因此在父进程中sleep(10),让子进程先执行,然后再执行父进程。

程序执行结果如下所示:

如何创建多个子进程呢?在开发并发服务器时,用到的进程池模型需要先创建指定书目的子进程。举个例子,假如我们现在需要创建2个子进程,很容易想到的是调用一个循环,执行fork函数2次即可。尝试一下是否可行呢?代码如下:

复制代码
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 #include <unistd.h>
 5 #include <errno.h>
 6 #include <sys/types.h>
 7 
 8 int main()
 9 {
10     int i;
11     pid_t pid;
12     printf("pid=%d , ppid=%d\n",getpid(),getppid());
13     //通过一个循环创建对个子进程
14     for(i=0;i<2;++i)
15     {
16         pid = fork();
17         if(pid == 0)
18         {
19             printf("create child process successfully.\n");
20             printf("pid=%d , ppid=%d\n",getpid(),getppid());
21             printf("i=%d\n",i);
22         }
23         else if(pid== -1)
24         {
25             perror("fork() error");
26             exit(-1);
27         }
28         else
29         {
30             sleep(3);
31             printf("parent process.\n");
32             printf("pid=%d , ppid=%d\n",getpid(),getppid());
33             printf("i=%d\n",i);
34         }
35     }
36 
37     exit(0);
38 }
复制代码

程序执行结果如下:

从结果来看,子进程的数目不是2而是3,这是为什么呢?先简单的分析一下:从结果看出父进程ID为10669,子进程的ID分别为:10670、10671、10672。

父子进程之间的关系如下:

ID为10670的子进程也调用fork函数,创建了一个进程。因为fork函数创建的进程是父进程的一份拷贝,保存了当前的数据空间、堆、栈及共享代码区域。正确的方式应该是在子进程中跳出,停止继续fork。改进的代码如下:

复制代码
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 #include <unistd.h>
 5 #include <errno.h>
 6 #include <sys/types.h>
 7 
 8 int main()
 9 {
10     int i;
11     pid_t pid;
12     printf("pid=%d , ppid=%d\n",getpid(),getppid());
13     for(i=0;i<2;++i)
14     {
15         pid = fork();
16         if(pid == 0)
17         {
18             printf("create child process successfully.\n");
19             printf("pid=%d , ppid=%d\n",getpid(),getppid());
20             printf("i=%d\n",i);
21             //子进程跳出循环,防止子进程继续创建子进程
22             break;
23         }
24         else if(pid== -1)
25         {
26             perror("fork() error");
27             exit(-1);
28         }
29         else
30         {
31             sleep(3);
32             printf("parent process.\n");
33             printf("pid=%d , ppid=%d\n",getpid(),getppid());
34             printf("i=%d\n",i);
35             //父进程继续创建子进程
36             continue;
37         }
38     }
39 
40     exit(0);
41 }
复制代码

程序执行结果如下:

 从结果可以看出这父进程(ID为10789)创建了两个子进程(ID分别为:10790、10791)。

现有有这样一个面试题,程序如下:

复制代码
 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <stdlib.h>
 4 #include <sys/types.h>
 5 
 6 int main()
 7 {
 8     pid_t   pid1;
 9     pid_t   pid2;
10 
11     pid1 = fork();
12     pid2 = fork();
13 
14     printf("pid1=%d,pid2=%d\n",pid1,pid2);
15     exit(0);
16 }
复制代码

要求如下:
 已知从这个程序执行到这个程序的所有进程结束这个时间段内,没有其它新进程执行。
 1、请说出执行这个程序后,将一共运行几个进程。
 2、如果其中一个进程的输出结果是“pid1:1001, pid2:1002”,写出其他进程的输出结果(不考虑进程执行顺序)。

  这个题目考查fork函数的理解。fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等)数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程,父子进程并行的执行剩下的部分。

程序的执行过程如下:

(1)程序开始执行时候系统分配一个进程进行执行,称该进程为主进程P,进程ID题目未给,

(2)主进程执行到第一个fork函数的时候,创建一个新的子进程P1,有题目可知进程ID为1001,fork函数有两个返回值,返回pid=0代表子进程P1,pid1>0代表父进程P。

(3)现在有两个进程P和P1,分别执行剩下部分,

(4)P进程(父进程,所以pid1=1001)调用fork创建子进程P2,返回两个值中pid2=1002表示P2的进程ID返回给父进程P,pid2=0子进程P2本身,所以输出pid1=1001,         pid2=1002和pid1=1001,pid2=0。

(5)P1进程(子进程,所以pid1=0)调用fork创建子进程P3,进程ID类推为1003,返回两个值中pid2=1003表示P3的进程ID返回给父进程P1,pid2=0标识进程P3本身。所以输出pid1=0,pid2=1003和pid1=0,pid2=0。

(6)执行整个结束。

根据以上分析可知答案:

1、一共执行了四个进程。(P0, P1, P2, P3)

2、另外几个进程的输出分别为:

 pid1:1001, pid2:0

 pid1:0, pid2:1003

 pid1:0, pid2:0

上机测试如下:

测试结果如下:

测试结果虽然不是1001,但是可以看出理论分析过程是正确的。

相关文章
|
C语言
C语言 父进程fork()出的多个子进程在结束后,父进程如何回收?
我在网上找了半天都是在说wait()和waitpid()的详解或者是单个子进程的回收。答非所问。 很简单,根据wait()或者waitpid()的函数特性。没有子进程时返回-1。
83 0
|
6月前
|
存储 算法 Unix
【创建进程】fork函数与写时拷贝
【创建进程】fork函数与写时拷贝
|
6月前
进程之 回收子进程之避免僵尸进程的产生
wait() 与 waitpid() 函数的使用.
71 0
|
程序员 Linux Shell
【CSAPP】进程控制 | 系统调用错误处理 | 进程状态 | 终止进程 | 进程创建 | 回收子进程 | 与子进程同步(wait/waitpid) | execve 接口
【CSAPP】进程控制 | 系统调用错误处理 | 进程状态 | 终止进程 | 进程创建 | 回收子进程 | 与子进程同步(wait/waitpid) | execve 接口
201 0
C++中fork函数的使用及原理
C++中fork函数的使用及原理,在C++中,fork函数用于创建一个新的进程称为子进程,该进程与原始进程几乎完全相同。 fork函数的基本概况 fork() 函数调用成功之后,会有两个返回值。当前进程,也就是父进程返回子进程的 pid,子进程返回 0。如果函数调用错误,返回为-1。 #include <stdio.h> #include <unistd.h> int main(void) { int i = 0; printf("i\tson/pa\tppid\tpid\tfpid\n"); for (i = 0; i < 2; i++) { p
314 0
|
存储 编译器 调度
fork函数、进程退出、进程等待(2)
 return是一种更常见的退出进程方法。执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返 回值当做 exit的参数。
146 0
|
缓存 Linux 调度
fork函数、进程退出、进程等待(1)
在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
208 0
|
算法 物联网 Linux
创建进程 fork 函数|学习笔记
快速学习创建进程 fork 函数
创建进程 fork 函数|学习笔记