zoukankan      html  css  js  c++  java
  • linux中fork()函数具体解释(原创!!实例解说)

     一、fork入门知识

         一个进程,包含代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程差点儿全然同样的进程,也就是两个进程能够做全然同样的事,但假设初始參数或者传入的变量不同,两个进程也能够做不同的事。
        一个进程调用fork()函数后,系统先给新的进程分配资源,比如存储数据和代码的空间。然后把原来的进程的全部值都拷贝到新的新进程中,仅仅有少数值与原来的进程的值不同。相当于克隆了一个自己。

         我们来看一个样例:

         运行结果是:
        i am the child process, my process id is 5574
        我是爹的儿子
        统计结果是: 1
        i am the parent process, my process id is 5573
        我是孩子他爹
        统计结果是: 1

        在语句fpid=fork()之前,唯独一个进程在运行这段代码,但在这条语句之后,就变成两个进程在运行了,这两个进程的差点儿全然同样,将要运行的下一条语句都是if(fpid<0)……
        为什么两个进程的fpid不同呢,这与fork函数的特性有关。fork调用的一个奇异之处就是它只被调用一次,却可以返回两次,它可能有三种不同的返回值:
        1)在父进程中,fork返回新创建子进程的进程ID;
        2)在子进程中,fork返回0;
        3)假设出现错误,fork返回一个负值;

        在fork函数运行完成后,假设创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们能够通过fork返回的值来推断当前进程是子进程还是父进程。

        引用一位网友的话来解释fpid的值为什么在父子进程中不同。“事实上就相当于链表,进程形成了链表,父进程的fpid(p 意味point)指向子进程的进程id, 由于子进程没有子进程,所以其fpid为0.
        fork出错可能有两种原因:
        1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN。
        2)系统内存不足,这时errno的值被设置为ENOMEM。
        创建新进程成功后,系统中出现两个基本全然同样的进程,这两个进程运行没有固定的先后顺序,哪个进程先运行要看系统的进程调度策略。
        每一个进程都有一个独特(互不同样)的进程标识符(process ID),能够通过getpid()函数获得,另一个记录父进程pid的变量,能够通过getppid()函数获得变量的值。
       
    fork运行完成后,出现两个进程,

        有人说两个进程的内容全然一样啊,怎么打印的结果不一样啊,那是由于推断条件的原因,上面列举的仅仅是进程的代码和指令,还有变量啊。
        运行完fork后,进程1的变量为count=0,fpid!=0(父进程)。进程2的变量为count=0,fpid=0(子进程),这两个进程的变量都是独立的,存在不同的地址中,不是共用的,这点要注意。能够说,我们就是通过fpid来识别和操作父子进程的。
        还有人可能疑惑为什么不是从#include处開始复制代码的,这是由于fork是把进程当前的情况拷贝一份,运行fork时,进程已经运行完了int count=0;fork仅仅拷贝下一个要运行的代码到新的进程。

    二、fork进阶知识

        先看一份代码:

        运行结果是:
        i son/pa ppid pid  fpid
        0 parent 2043 3224 3225
        0 child  3224 3225    0
        1 parent 2043 3224 3226
        1 parent 3224 3225 3227
        1 child     1 3227    0
        1 child     1 3226    0

        这份代码比較有意思,我们来认真分析一下:
        第一步:在父进程中,指令运行到for循环中,i=0,接着运行fork,fork运行完后,系统中出现两个进程,各自是p3224和p3225(后面我都用pxxxx表示进程id为xxxx的进程)。能够看到父进程p3224的父进程是p2043,子进程p3225的父进程正好是p3224。我们用一个链表来表示这个关系:
        p2043->p3224->p3225
        第一次fork后,p3224(父进程)的变量为i=0,fpid=3225(fork函数在父进程中返向子进程id),代码内容为:

        p3225(子进程)的变量为i=0,fpid=0(fork函数在子进程中返回0),代码内容为:

        所以打印出结果:
        0 parent 2043 3224 3225
        0 child  3224 3225    0

        第二步:如果父进程p3224先运行,当进入下一个循环时,i=1,接着运行fork,系统中又新增一个进程p3226,对于此时的父进程,p2043->p3224(当前进程)->p3226(被创建的子进程)。
        对于子进程p3225,运行完第一次循环后,i=1,接着运行fork,系统中新增一个进程p3227,对于此进程,p3224->p3225(当前进程)->p3227(被创建的子进程)。从输出能够看到p3225原来是p3224的子进程,如今变成p3227的父进程。父子是相对的,这个大家应该easy理解。仅仅要当前进程运行了fork,该进程就变成了父进程了,就打印出了parent。
        所以打印出结果是:
        1 parent 2043 3224 3226
        1 parent 3224 3225 3227
     
        第三步:第二步创建了两个进程p3226,p3227,这两个进程运行完printf函数后就结束了,由于这两个进程无法进入第三次循环,无法fork,该运行return 0;了,其它进程也是如此。
        下面是p3226,p3227打印出的结果:
        1 child     1 3227    0
        1 child     1 3226    0

        细心的读者可能注意到p3226,p3227的父进程难道不该是p3224和p3225吗,怎么会是1呢?这里得讲到进程的创建和死亡的过程,在p3224和p3225运行完第二个循环后,main函数就该退出了,也即进程该死亡了,由于它已经做全然部事情了。p3224和p3225死亡后,p3226,p3227就没有父进程了,这在操作系统是不被同意的,所以p3226,p3227的父进程就被置为p1了,p1是永远不会死亡的,至于为什么,这里先不介绍,留到“三、fork高阶知识”讲。
        总结一下,这个程序运行的流程例如以下:

         这个程序终于产生了3个子进程,运行过6次printf()函数。
        我们再来看一份代码:

         它的运行结果是:
        father
        son
        father
        father
        father
        father
        son
        son
        father
        son
        son
        son
        father
        son
        这里就不做详解了,仅仅做一个大概的分析。
        for        i=0         1           2
                  father     father     father
                                            son
                                son       father
                                            son
                   son       father     father
                                            son
                                son       father
                                            son
        当中每一行分别代表一个进程的运行打印结果。
        总结一下规律,对于这样的N次循环的情况,运行printf函数的次数为2*(1+2+4+……+2N-1)次,创建的子进程数为1+2+4+……+2N-1个。(感谢gao_jiawei网友指出的错误,原本我的结论是“运行printf函数的次数为2*(1+2+4+……+2N)次,创建的子进程数为1+2+4+……+2N ”,这是错的)
        网上有人说N次循环产生2*(1+2+4+……+2N)个进程,这个说法是不正确的,希望大家须要注意。

        数学推理见http://202.117.3.13/wordpress/?p=81(该博文的最后)。
        同一时候,大家假设想測一下一个程序中究竟创建了几个子进程,最好的方法就是调用printf函数打印该进程的pid,也即调用printf("%d/n",getpid());或者通过printf("+/n");来推断产生了几个进程。有人想通过调用printf("+");来统计创建了几个进程,这是不妥当的。详细原因我来分析。
        老规矩,大家看一下以下的代码:

        执行结果例如以下:
        fork!
        I am the parent process, my process id is 3361
        I am the child process, my process id is 3362
        假设把语句printf("fork!/n");凝视掉,执行printf("fork!");
        则新的程序的执行结果是:
        fork!I am the parent process, my process id is 3298
        fork!I am the child process, my process id is 3299
        程序的唯一的差别就在于一个/n回车符号,为什么结果会相差这么大呢?
        这就跟printf的缓冲机制有关了,printf某些内容时,操作系统不过把该内容放到了stdout的缓冲队列里了,并没有实际的写到屏幕上。可是,只要看到有/n 则会马上刷新stdout,因此就马上可以打印了。
        执行了printf("fork!")后,“fork!”只被放到了缓冲里,程序执行到fork时缓冲里面的“fork!”  被子进程复制过去了。因此在子进程度stdout缓冲里面就也有了fork! 。所以,你终于看到的会是fork!  被printf了2次!!!!
        而执行printf("fork! /n")后,“fork!”被马上打印到了屏幕上,之后fork到的子进程里的stdout缓冲里不会有fork! 内容。因此你看到的结果会是fork! 被printf了1次!!!!
        所以说printf("+");不能正确地反应进程的数量。
        大家看了这么多可能有点疲倦吧,不过我还得贴最后一份代码来进一步分析fork函数。

        问题是不算main这个进程自身,程序究竟创建了多少个进程。
        为了解答这个问题,我们先做一下弊,先用程序验证一下,到此有多少个进程。

        答案是总共20个进程,除去main进程,还有19个进程。
        我们再来细致分析一下,为什么是还有19个进程。
        第一个fork和最后一个fork肯定是会运行的。
        主要在中间3个fork上,能够画一个图进行描写叙述。
        这里就须要注意&&和||运算符。
        A&&B,假设A=0,就没有必要继续运行&&B了;A非0,就须要继续运行&&B。
        A||B,假设A非0,就没有必要继续运行||B了,A=0,就须要继续运行||B。
        fork()对于父进程和子进程的返回值是不同的,依照上面的A&&B和A||B的分支进行绘图,能够得出5个分支。

       

         加上前面的fork和最后的fork,总共4*5=20个进程,除去main主进程,就是19个进程了。

    三、fork高阶知识

            这一块我主要就fork函数讲一下操作系统进程的创建、死亡和调度等。由于时间和精力限制,我先写到这里,下次找个时间我争取把剩下的内容补齐。

    參考资料:

          http://blog.csdn.net/dog_in_yellow/archive/2008/01/13/2041079.aspx

          http://blog.chinaunix.net/u1/53053/showart_425189.html

          http://blog.csdn.net/saturnbj/archive/2009/06/19/4282639.aspx

          http://www.cppblog.com/zhangxu/archive/2007/12/02/37640.html

          http://www.qqread.com/linux/2010/03/y491043.html

          http://www.yuanma.org/data/2009/1103/article_3998.htm

  • 相关阅读:
    HDU 1525
    kmp模板
    hdu 4616 Game(树形DP)
    hdu 4619 Warm up 2(并查集活用)
    hdu 4614 Vases and Flowers(线段树加二分查找)
    Codeforces 400D Dima and Bacteria(并查集最短路)
    poj 2823 Sliding Window (单调队列)
    hdu 2196 Computer(树形dp)
    hdu 4604 Deque
    最短路径
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/4075970.html
Copyright © 2011-2022 走看看