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

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

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

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

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

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

程序执行结果如下所示:

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

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

程序执行结果如下:

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

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

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

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

程序执行结果如下:

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

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

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

Unix环境高级编程(七)fork函数总结的更多相关文章

  1. Unix环境高级编程:fork, vfork, clone

    fork fork产生的子进程是传统意义上的进程,fork之后执行路径就互不关联了,一旦fork返回后面的代码就在不用的进程上下文中执行了.到底是子进程先执行还是父进程先执行一般是随机的或者依赖实现的 ...

  2. (七) 一起学 Unix 环境高级编程(APUE) 之 进程关系 和 守护进程

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  3. UNIX环境高级编程——sigqueue、sigsuspend函数

    一.sigqueue函数 功能:新的发送信号系统调用,主要是针对实时信号提出的支持信号带有参数,与函数sigaction()配合使用. int sigqueue(pid_t pid, int sig, ...

  4. UNIX环境高级编程——TCP/IP网络编程 常用网络信息检索函数

    UNIX环境高级编程——TCP/IP网络编程   常用网络信息检索函数 gethostname()   getppername()   getsockname()   gethostbyname() ...

  5. (六) 一起学 Unix 环境高级编程 (APUE) 之 进程控制

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  6. (八) 一起学 Unix 环境高级编程 (APUE) 之 信号

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  7. (九) 一起学 Unix 环境高级编程 (APUE) 之 线程

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  8. (十) 一起学 Unix 环境高级编程 (APUE) 之 线程控制

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  9. (十一) 一起学 Unix 环境高级编程 (APUE) 之 高级 IO

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

随机推荐

  1. Android组件之BroadCast简单实践

    作为Android的四大组件之一,没有理由不介绍一下BroadCast,BroadCast中文简单翻译就是广播,前阵子浙江某大学的啦啦操,广场舞的大妈,其中大妈和学生从喇叭和音响上听到的声音就是事件源 ...

  2. HTML5, CSS3, ES5新的web标准和浏览器支持一览 转

    本文整理了一些最重要(或者说人气比较高罢)的新标准,虽然它们多数还只是w3c的草案,离Recommendation级别还早,却已经成为新一轮浏览器大战中备受追捧的明星,开发者社区里也涌现出大量相关的d ...

  3. 在Android中使用Android Ksoap2调用WebService

    一.WebService介绍 WebService是基于SOAP协议可实现web服务器与web服务器之间的通信,因采用SOAP协议传送XML数据具有平台无关性,也是成为解决异构平台之间通信的重要解决方 ...

  4. 手机WiFi万能钥匙查看破解的password和手机查询命令收集

    手机须要网络利用WiFi万能钥匙破解了WIFI的password.手机就能够上网了,但假设想在电脑上使用手机破解的Wifi热点上网就须要password,此时须要知道手机破解的password,WiF ...

  5. 网络结构设计——负载均衡之LVS学习笔记(三)

    LVS按个人理解的说就是将一台Linux服务器当作路由器等功能的技术.LVS---Linux虚拟服务器. LVS实现了三种IP负载均衡技术VS/NAT.VS/TUN.VS/DR. 今天简单分享一下我在 ...

  6. uva 10670 Work Reduction(贪心)

    题目连接:10670 - Work Reduction 题目大意:有tol的工作量,和要求达到的工作剩余量sur,然后是公司总数,对应每个公司提供两种服务,1.完成一个工作量,2.完成当前未完成工作量 ...

  7. PHP採集利器:依据開始字符串和结束字符串截取须要的採集内容数据

    PHP採集利器:依据開始字符串和结束字符串截取须要的採集内容数据 function strCutByStr(&$str, $findStart, $findEnd = false, $enco ...

  8. 通过修改css文件来观察openerp表单中的col和colspan

    适用版本 openerp 6.1.1 问题的提出 在openerp的表单定义中, 要使用 colspan和col 指你定各个元素的占位, 前者说明了本元素占据其所在容器的列数, 后者说明了本元素作为容 ...

  9. eclipse 修改maven项目的jdk版本

      eclipse 修改maven项目的jdk版本 CreationTime--2018年6月8日10点29分 Author:Marydon 1.情景展示 jdk版本太低,如何修改 2.错误方式 第一 ...

  10. jquery实现下拉联动

    很多项目用到这个功能,虽然写了不下5次以上了,一直没做过记录,记录一下,下次直接拷贝了,免得还得要重复写浪费时间. 先上HTML代码: 品牌: <select class="sa&qu ...