• fork函数总结


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

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

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

    View Code
     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次即可。尝试一下是否可行呢?代码如下:

    View Code
     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。改进的代码如下:

    View Code
     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,但是可以看出理论分析过程是正确的。

    题目来自:http://www.cnblogs.com/leoo2sk/archive/2009/12/11/talk-about-fork-in-linux.html

  • 相关阅读:
    Swift入门篇-Hello World
    Swift入门篇-swift简介
    Minecraft 插件 world edit 的cs 命令
    搭建本地MAVEN NEXUS 服务
    MC java 远程调试 plugin 开发
    企业内部从零开始安装docker hadoop 提纲
    javascript 命令方式 测试例子
    ca des key crt scr
    JSF 抽象和实现例子 (函数和属性)
    form 上传 html 代码
  • 原文地址:https://www.cnblogs.com/Anker/p/2820526.html
Copyright © 2020-2023  润新知