此文原文来源于一个blog,文章的名称为:linux fork函数的精辟解说原文地址:http://blog.chinaunix.net/space.php?uid=12461657&do=blog&id=3062996感觉这篇文章不错,在此分享下来,在原文的基础上增加了自己的一些理解和说明。

  开始演示:[plain]view plaincopyprint?

  [root@test code]# cat fork.c #include <stdio.h> #include <unistd.h> #include <sys/types.h>

  main()

  { pid_t pid;pid=fork();if (pid < 0)

  printf("Error in fork!");else if (pid == 0)

  printf("I am the child process. The process id is %d\n",getpid());else printf("I am the parent process. The process id is %d\n",getpid());} [root@test code]# gcc fork.c -o fork [root@test code]# ./fork I am the child process. The process id is 6260 I am the parent process. The process id is 6259先说什么是进程一个进程,就是一个可执行程序的一次执行过程中的一个状态。

  打个比方

  我们把一个大学数学老师比作一个可执行程序,老师就是一个人,相当于一份源码,这个数学老师每个学期可能要教多个班,假设他教1班和2班,时间是一个学期,那么他从开学到期末教这两个班这个过程就是两个进程,两个进程的周期都是一个学期。

  在稍微理解了进程的概念之后,我们说在正在运行的计算机中,不管是Linux或者是Windows系统都运行有很多进程,虽然我们的系统中进程比较多,但是对于单核的CPU而言,每一时刻只能有一个进程占用CPU,其他的进程就可能处在等待执行、就绪、结束等状态(状态名称可能随不同操作系统而相异)。

  那么要使我们的操作系统能够正常执行,操作系统对这些进程的管理,典型的情况,是通过内存中的进程表完成的(这里的进程表不是源码的意思)。进程表中的每一个表项,记录的是当前操作系统中一个进程的情况,比如执行到哪里,下一步要执行什么命令等。

  一个称为“程序计数器(program counter, pc)”的寄存器,指出当前占用CPU的进程要执行的下一条指令的位置。当分给某个进程的CPU时间已经用完,操作系统将该进程相关的寄存器的值,保存到该进程在进程表中对应的表项里面;然后把将要接替掉当前进程的那个进程的上下文从内存的进程表中读入,并更新相应的寄存器。这个过程称为“上下文交换(process context switch)”,实际的上下文交换需要涉及到更多的数据,那和fork无关,不再多说,主要要记住程序寄存器pc指出程序当前已经执行到哪里,是进程上下文的重要内容,换出 CPU的进程要保存这个寄存器的值,换入CPU的进程,也要根据进程表中保存的本进程执行上下文信息,更新这个寄存器)。

  好了,有这些概念打底,可以说fork了。当你的程序执行到下面的语句:pid=fork();操作系统会进行如下的大概过程:创建一个新的子进程,而这个子进程是父进程的副本,接下来这两个进行就由操作系统调度,直到程序执行结束。

  那么这个过程,我们可以对其进行更加详细的分析。在执行fork以后,操作系统复制一份当前执行的进程的数据,包括进程的数据空间、堆和栈等,并且在进程表中相应为它建立一个新的表项。上下文也是原进程(父进程)的拷贝。但是父、子进程共享正文段,也就是CPU执行的机器指令部分,这个可共享的,在存储器中只需要一个副本,而且这个副本通常是只读的。

  那在什么时候父子进程中就分道扬镳呢?

  从上面的实验结果我们看出那两条打印出来的语句不是在同一个进程里面的,因为在同一个进程里面不可能存在,不通结果的getpid(),事实说明是两个不同进程返回的结果,那么在执行pid=fork()以后,开始出现父、子进程,既然是两个进程,那么接下来谁先被调度,也就是说执行pid=fork()以后,在单核CPU下,只会有一个进程被调度,假设是我们的父进程占用CPU时间,父进程继续执行,操作系统对fork的实现,使这个调用在父进程中返回刚刚创建的子进程的pid(一个正整数),所以下面的if语句中pid<0, pid==0的两个分支都不会执行。所以输出I am the parent process……。

  子进程在之后的某个时候得到调度,它的上下文被换入。我们上面分析过,在子进行创建的时候也会复制父进程的上下文,所以子进程不会从头开始执行,而是从pid=fork()开始执行,基于操作系统对fork的实现,使得子进程中fork调用返回0.所以在这个进程(中pid=0.这个进程继续执行的过程中,if语句中 pid<0不满足,但是pid==0是true.所以输出I am the child process……。

  我们下面来看一个和我在上面分析的一个结论似乎存在矛盾的现象

  [plain]view plaincopyprint?

  [root@test code]# cat fork.c #include <stdio.h> #include <unistd.h> #include <sys/types.h>

  main()

  { pid_t pid;printf("fork!");pid=fork();if (pid < 0)

  printf("Error in fork!\n");else if (pid == 0)

  printf("I am the child process. The process id is %d\n",getpid());else printf("I am the parent process. The process id is %d\n",getpid());} [root@test code]# gcc fork.c -o fork [root@test code]# ./fork fork!I am the child process. The process id is 7378 fork!I am the parent process. The process id is 7377 [root@test code]#

  这里我添加了printf("fork!")这一行,执行了以后我们发现,“fork!”打印了两次,我们上面不是说,fork以后的子进程的上下文不是和父进程一样吗,也就是说子进程不会从头开始执行,应该从fork执行,那么fork!的出现不是有矛盾吗?我们再来看看下面的现象

  [plain]view plaincopyprint?

  <span style="font-family:Courier New;">[root@test code]# cat fork.c #include <stdio.h> #include <unistd.h> #include <sys/types.h>

  main()

  { pid_t pid;printf("fork!\n");pid=fork();if (pid < 0)

  printf("Error in fork!\n");else if (pid == 0)

  printf("I am the child process. The process id is %d\n",getpid());else printf("I am the parent process. The process id is %d\n",getpid());} [root@test code]# gcc fork.c -o fork [root@test code]# ./fork fork!

  I am the child process. The process id is 7458 I am the parent process. The process id is 7457 [root@test code]#</span>

  这里我在printf("fork!")这一行的fork!后面添加了一个换行符,变成printf("fork!\n")执行以后发现只打印一个fork!这个到底是什么原因呢?

  主要的区别是因为有了一个\n 回车符号,说起真正的原因,这和printf的缓冲机制有关了,printf某些内容时,操作系统仅仅是把该内容放到了stdout的缓冲队列里了,并没有实际的写到屏幕上,但是只要看到有\n 则会立即刷新stdout,因此就马上能够打印了。

  还没有执行到fork()的时候,原进程运行了printf("fork!") 后,fork!仅仅被放到了缓冲里,执行了fork,这时候子进程复制一份父进程的数据,包括这个stdout缓冲,在子进程度stdout缓冲里面就也有了fork!。执行到后面这两个缓冲都打印到屏幕上,所以出现两次,并不是printf执行两次。而运行 printf("fork!\n")后, fork!被立即打印到了屏幕上,之后fork到的子进程里的stdout缓冲里不会有fork!内容,因此你看到的结果会是fork!被printf了1次!!!

linux fork函数的精辟解说相关推荐

  1. Linux fork()函数底层CopyOnWrite写时复制实现原理剖析

    创建子进程的速度应该是怎么样的? 如果父进程是redis,内存数据比如说有10G,需要考虑的有两个点: ①速度如何 ②内存空间够不够 在Linux中有个系统调用--fork(): ①速度:快 ②空间: ...

  2. linux fork函数及其简单应用

    基础理论 在 Linux 中,fork() 函数是一个非常重要的系统调用,它是创建新进程的主要方式.它会创建一个新的进程,该进程与父进程几乎完全相同,包括代码段.数据段和堆栈等.但是,新的进程有自己的 ...

  3. Unix/Linux fork()函数的理解

    作者:王姗姗,华清远见嵌入式学院讲师. 对于刚刚接触Unix/Linux操作系统,在Linux下编写多进程的人来说,fork是最难理解的概念之一:它执行一次却返回两个值. 首先我们来看下fork函数的 ...

  4. fork是linux函数吗,linux fork()函数

    概述 最近在看进程间的通信,看到了fork()函数,虽然以前用过,这次经过思考加深了理解.现总结如下: 1.函数本身 (1)头文件 #include #include (2)函数原型 pid_t fo ...

  5. linux fork函数详解

    引言 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做 ...

  6. linux fork 函数,Linux的fork()系统调用

    Linux的fork()系统调用,就是以父进程为模版创建子进程,是Linux系统的进程管理机制的核心API之一,另一个是调度器函数schedule(),它的用户态API就是之前说自旋锁时提到的sche ...

  7. linux fork函数浅析

    #include <sys/types.h> #include <unistd.h> /* 功能:复制进程 參数:无 返回值: 成功: 父进程:返回子进程id 子进程:返回0 ...

  8. Linux基础学习系列:对于fork()函数的学习,及进程创建相关知识

    fork()函数 :由当前进程再生成一个进程出来 #include <sys/types.h> #include <unistdh> pid_t fork(void); 返回: ...

  9. 深入解析Linux中的fork函数

    1.定义 #include <unistd.h> #include<sys/types.h> pid_t fork( void ); pid_t 是一个宏定义,其实质是int, ...

最新文章

  1. 科技奥运再进一步,北京冬奥组委携手阿里云启动“云上转播”
  2. win7-X64下用VM安装linux系统CentOS
  3. pyhanlp 命名实体识别
  4. 一文梳理多任务学习(MMoE/PLE/DUPN/ESSM等)
  5. php forms 上传更新json_通达OA任意文件上传漏洞详细分析
  6. 网易云深度学习第二课notebook1
  7. qt插件开发框架搭建_Flutter框架之:开发环境搭建
  8. 成功转型机器学习,多年的经验分享
  9. shell脚本中执行python脚本并接收其返回值的例子
  10. 盘点一款手机Python编程神器——AidLearning
  11. 综合评价与决策方法02——模糊综合评判法
  12. 经纬度WGS84地理坐标系转换成CGCS2000坐标系步骤
  13. redis报错Unrecoverable error: corrupted cluster config file.
  14. mscorsvw.exe进程占用CPU资料80%以上的原因
  15. 编程修养 - 来自网络整理
  16. 杰理之提示音配置【篇】
  17. 判断素数的五种方法最全埃筛素数欧筛素数孪生素数
  18. 中国道路、居民地、面状水系、铁路、线状水系
  19. C语言软件版本号宏定义的高级技巧
  20. leetcode系列-617.合并二叉树

热门文章

  1. android 卡片旋转动画,Android 卡片翻转效果
  2. Python中单元测试出错了,会怎么样?
  3. 【mac开发环境】第1篇:ITerm2+Oh My Zsh
  4. 第一章 docker 镜像,容器,仓库基本命令(二)
  5. [翻译]NUnit---String Collection File Directory Assert (七)
  6. Android Library projetcts cannot be exported.
  7. 解决Nginx与mysql勾结的错误
  8. CONTROLLER_NAME 常量 当前访问的控制器
  9. 51CTO的技术门诊谈OSSIM
  10. 网络工程师需要具备哪些素质或专业技术