精华内容
下载资源
问答
  • fork

    千次阅读 2019-10-27 15:06:53
    fork函数 fork函数从已经存在的进程中创建一个新的进程,新进程为子进程,原进程为父进程。子进程返回0,父进程返回子进程的pid。

    fork函数

    fork函数从已经存在的进程中创建一个新的进程,新进程为子进程,原进程为父进程。子进程返回0,父进程返回子进程的pid。

     #include<stdio.h>
     #include<unistd.h>
     #include<sys/types.h>
     int main()
     {
          int ret = fork();
          if(ret == 0)
          {
              printf("I am child : %d, ret: %d\n", getpid(), ret);
          }
          else if(ret > 0)
          {
              printf("I am father : %d, ret: %d\n", getpid(), ret);
          }
          else
          {
              perror("fork");
              return 1;
          }
          return 0;
      }
    

    进程调用fork,当控制转移到内核中的fork代码后,内核:

    1. 分配新的内存块和内核数据结构给子进程
    2. 将父进程的部分数据结构内容拷贝至子进程
    3. 添加子进程到系统进程列表当中
    4. fork返回,开始调度器调度

    写时拷贝

    父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)。
    在这里插入图片描述
    写时拷贝是一种采取了惰性优化方法来避免复制时的系统开销,多个进程读取同一份数据,如果全部复制会造成浪费,所以每个进程只要保存一个指向这个资源的指针就可以了,如果一个进程要修改自己的那份资源,那么就会复制那份资源,并把复制的那份提供给进程。

    展开全文
  • Unix/Linux fork前传

    万次阅读 多人点赞 2019-09-09 08:15:00
    本文是《Linux fork那些隐藏的开销》的前传。fork的由来fork的思想在UNIX出现几年前就出现了,时间大概是1963年,这比UNIX在PDP-7上的第一个版本...

    本文是《Linux fork那些隐藏的开销》的前传。

    fork的由来

    fork的思想在UNIX出现几年前就出现了,时间大概是1963年,这比UNIX在PDP-7上的第一个版本早了6年。

    1963年,计算机科学家Melvin Conway(以Conway’s Law闻名于世)写下一篇论文,正式提出了fork思想,该论文链接:A Multiprocessor System Design:

    https://archive.org/details/AMultiprocessorSystemDesignConway1963/page/n7

    fork的思想最初是Conway作为一种 多处理器并行 的方案提出来的,这个想法非常有意思。简而言之,fork思想来源于流程图。

    我们看一个普通的流程图:640?wx_fmt=png

    你看,流程图的分枝处,fork-叉子,多么形象!

    一个流程图上的分支点分裂出来的分支显然是逻辑独立的,这便是可并行的前提,于是它们便可以表现为不同的 处理进程(process) 的形式,当时的表达还只是“process”这个术语,它还不是现代操作系统意义上的“进程”的概念。

    join同步点表现为多个并行处理的进程由于某种原因不得不同步的点,也就是多个并行流程汇合的点,直到现在,在多线程编程中,这个点依然叫join。比如Java Thread的join方法以及pthread库的pthread_join函数。

    广义来讲,join也表示诸如临界区等必须串行通过的点, 减少join点的数量将会提高并行的效率。

    我们来看看Conway论文中关于fork的原始图示:640?wx_fmt=png

    Conway在论文中的另一个创举是,他将处理进程(也就是后来操作系统中的process的概念)以及执行该进程的处理器(即CPU核)分离了开来,抽象出了schedule层。

    大意是说, “只要满足系统中的活动处理器数量是总处理器数量和并行处理进程的最小值即可。” 这意味着调度程序可以将多处理器系统的所有处理器和系统所有处理进程分别看作是统一的资源池和消费者,执行统一调度:640?wx_fmt=png在UNIX引入fork之后,这种多处理器并行的设计思想就深入到了UNIX的核心。这个思想最终也影响了UNIX以及后来的Linux,直到现在。

    关于这个设计思想为什么可以影响UNIX这么久,我想和Conway本人的“Conway’s law”不无关系,在这个law中,他提到:Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization’s communication structure.

    好了,fork本身的由来我们已经了解,就像做菜一样,现在我们把它放在一边备用。

    花开两朵,各表一枝。接下来看UNIX fork的另一个脉络。

    早期UNIX的覆盖(overlaying)技术

    1969年最初的UNIX用一种在现在看来非常奇怪的方式运行。

    一般的资料都是从UNIX v6版本开始讲起,那个版本已经是比较 “现代” 的版本了,所以很少有人能看到最初的UNIX是什么样子的。即便是能查阅到的1970年的PDP-7上运行的UNIX源码,也是引入fork之后的版本,在那之前的最原始版本几乎找不到了(你可能会说,那时的UNIX不叫UNIX,but who cares…)。

    1969年的汤普森版UNIX超级简陋,这可以在Dennis M. Ritchie的一篇论文中见一斑:The Evolution of the Unix Time-sharing System:

    http://www.read.seas.harvard.edu/~kohler/class/aosref/ritchie84evolution.pdf

    最初的UNIX是一个分时系统,它只有两个shell进程,分别属于两个终端:640?wx_fmt=png

    分时系统最初并不是基于进程分时的,那时根本还没有完整的进程的概念,分时系统是针对终端分时的,而操作员坐在终端前,为了让每个操作员在操作过程中感觉上是在独占机器资源,每个终端享受一段时间的时间片,在该时间片内,该终端前的操作员完全享受机器,但是为了公平,超过了时间片,时间片就要给另一个终端。

    就是这样,最初的UNIX为了体现分时特性,实现了最少的两个终端。注意,最初的UNIX没有fork,没有exec,甚至没有多进程的概念,为了实现分时,系统中仅有两个朴素的shell进程。

    事实上,最初的UNIX用只有两个元素的表来容纳所有进程(显然,这看起来好笑…),当然,这里的 “表” 的概念也是抽象的朴素概念,因为当时的系统是用PDP-7的汇编写的,还没有后来C语言数据结构。

    我们现在考虑其中一个终端的shell进程如何工作。马上问题就来了, 这个shell进程如何执行别的命令程序??

    如果说系统中最多只能容纳两个进程,一个终端只有一个shell进程的话,当该终端的shell进程执行其它命令程序时,它自己怎么办?这个问题得思考一会儿…

    注意,不要用现代的眼光去评价1969年的初版UNIX,按照现代的眼光,执行一个程序必然要生成一个新的进程,显然这在初版UNIX中并不正确。

    答案是根本不用产生新的进程,直接将命令程序的代码载入内存并 覆盖 掉shell进程的代码即可!当命令执行完后,再用shell的代码覆盖掉命令程序的代码,针对单独的终端,系统其实一直在执行下面的覆盖循环(摘自论文的Process control 章节):640?wx_fmt=png

    然而,在fork被引入UNIX之前,事实就是这样。一个终端上一直都是那一个进程,一会儿它执行shell的代码,一会儿它执行具体命令程序的代码,以下是一个覆盖程序的结构(图片来自《FreeBSD操作系统设计与实现》一书):640?wx_fmt=png

    然而,当时毕竟还没有将这个逻辑封装成exec系统调用,这些都是每一个进程显式完成的:

    • 对于shell执行命令程序而言,shell自己执行disk IO来载入命令程序覆盖掉自身;

    • 对于命令程序执行结束时,exit调用内部执行disk IO载入shell程序。

    exec逻辑是shell程序的一部分,由于它会被所有的命令程序所使用,该逻辑也被封装到了exit调用中。

    fork引入UNIX前的表象

    好了,目前为止,我们看完了两条线索:

    1. 1963年Melvin Conway提出了fork思想,作为在多处理器中并行执行进程的一个手段。

    2. 1969年汤普森版UNIX仅有两个shell进程,使用覆盖(overlaying)技术执行命令。

    截止目前,我们看到的表象是:

    • 汤普森版UNIX没有fork,没有exec,没有wait,仅有的库函数般的exit也和现在的exit系统调用大相径庭,显然汤普森版UNIX并非一个多进程系统,而只是一个可以跑的简陋的两终端分时系统!

    UNIX fork的诞生

    fork是如何引入UNIX的呢?

    这还要从采用覆盖技术的汤普森版UNIX所固有的问题说起,还是看论文原文:640?wx_fmt=png

    若要解决这些问题,很简单的方案汤普森都想到了:

    • 保持shell进程的驻留而不是销毁。命令执行时,将其交换到磁盘便是了

    很显然,命令程序是不能覆盖掉shell进程了。解决方案是使用 “交换” 技术。

    交换技术和覆盖技术其实都是解决有限内存的多进程使用问题的,不同点在于方向不同:

    • 覆盖技术指的是用不同的进程磁盘映像覆盖当前的进程内存映像。

    • 交换技术指的是用将进程的内存映像交换到磁盘,载入一个别的进程磁盘映像。

    使用交换技术解决覆盖的问题,意味着要创建新的进程:

    • 在新的进程中执行命令程序。

    UNIX需要进行改动,两个配额的进程表显然不够用了。当然,解决方案也并不麻烦:640?wx_fmt=png

    要讲效率,创造不如抄袭,创建新进程的最直接的就是copy当前shell进程,在copy的新进程中执行覆盖,命令程序覆盖copy的新进程,而当前的终端shell进程则被交换到磁盘保得全身。

    覆盖和交换相结合了,UNIX离现代化更近了一步!

    确定了copy当前进程的方案后,进一步的问题是如何来copy进程。

    现在要说回fork了。

    Conway提出fork思想后,马上就有了fork的实现原型(正如Conway自己所说,他只是提出了一个可能造就存在的想法,并没有实现它),Project Genie算是实现fork比较完善的系统之一了。

    Project Genie系统的fork不仅仅是盲目地copy进程,它对fork的过程拥有精细的控制权,比如分配多大的内存空间,copy哪些必要的资源等等。显然,Project Genie的fork是冲着Conway的多处理器并行逻辑去的。

    还是那句话,创造不如抄袭,UNIX若想实现进程copy,有一个现成的模版就是Project Genie,但是Project Genie的fork对于UNIX太过复杂,太过精细化了,UNIX显然用不到这些精细的控制, UNIX仅仅是想让fork出来的新进程被覆盖,而不是让它去执行什么多处理器上的并行逻辑。

    换句话说,UNIX只是借用了fork的copy逻辑的实现,来完成一件别的事。

    于是,UNIX非常粗暴的实现了fork!即完全copy父进程,这就是直到现在我们依然在使用的fork系统调用:640?wx_fmt=png

    取了个巧,奇技淫巧:

    • fork本来就不是让你用来覆盖新进程的,不然为何多此一举。fork是让你来分解程序流程得以并行处理的。

    UNIX fork就此诞生!

    我们再次回顾一下UNIX fork诞生之前的景象:640?wx_fmt=png

    再来看看fork诞生之后的景象:640?wx_fmt=png640?wx_fmt=png

    于是UNIX正式迈开了现代化建设的步伐,一直走到了今天。

    UNIX fork-exec

    关于exec,故事没什么好讲的,它事实上就是关于上述覆盖逻辑的封装,此后程序员不必自己写覆盖逻辑了,直接调用exec系统调用即可。

    于是经典的UNIX fork-exec序列便形成了。

    UNIX fork/exec/exit/wait

    值得一提的是,fork被引入UNIX后,exit的语义发生了巨大的改变。

    在原始的1969年汤普森版UNIX中,由于每一个终端有且仅有一个进程,这意味着覆盖永远是在shell程序和某个命令程序之间进行的:

    • shell执行命令A:命令程序A覆盖内存中的shell代码。

    • 命令A执行结束:shell覆盖结束的命令A的内存代码。

    然而,在fork被引入后,虽然shell执行某个命令依然是特定的命令程序覆盖fork出来的shell子进程,但是当命令执行完毕后,exit逻辑却不能再让shell覆盖当前命令程序了,因为shell从来就没有结束过,它作为父进程只是被交换到了磁盘而已(后来内存到了,可以容纳多个进程时,连交换都不需要了)。

    那么exit将让谁来覆盖当前进程呢?

    答案是不用覆盖,按照exit的字面意思,它只要结束自己就可以了。

    本着 自己的资源自己管理的责任原则 exit只需要清理掉自己分配的资源即可。比如清理掉自己的内存空间以及一些其它的数据结构。

    对于子进程本身而言,由于它是父进程生成的,所以它便由父进程来管理释放。于是经典的UNIX进程管理四件套正式形成:640?wx_fmt=png                                                   (完)

    浙江温州皮鞋湿,下雨进水不会胖!

    查看我们精华技术文章请移步: Linux阅码场原创精华文章汇总

    更多精彩,尽在"Linux阅码场",扫描下方二维码关注

    640?wx_fmt=png

    感谢您的耐心阅读,请随手转发一下或者点个“在看”吧~

    展开全文
  • test_fork:测试fork-源码

    2021-05-21 12:55:41
    test_fork 测试fork
  • fork一个进程,fork()函数fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,这个新产生的进程称为子进程。一个进程调用fork()函数后,系统先给新的进程...
  • Linux中fork()函数详解

    万次阅读 多人点赞 2018-08-12 04:42:38
    linux中fork()函数详解 一、fork入门知识 一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程, 也就是两个进程可以做完全相同的事,但如果初始...

     

    Linux中fork()函数详解

     

    一、fork入门知识

     

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

    一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。

    我们来看一个例子:

     

    /*
    *  fork_test.c
    *  version 1
    *  Created on: 2010-5-29
    *      Author: wangth
    */
    
    #include <unistd.h>
    #include <stdio.h>
    
    int main ()
    {
        pid_t fpid; //fpid表示fork函数返回的值
        int count = 0;
        fpid = fork();
        if (fpid < 0)
            printf("error in fork!");
        else if (fpid == 0)
        {
            printf("i am the child process, my process id is %d/n", getpid());
            printf("我是爹的儿子/n");//对某些人来说中文看着更直白。
            count++;
        }
        else
        {
            printf("i am the parent process, my process id is %d/n", getpid());
            printf("我是孩子他爹/n");
            count++;
        }
        printf("统计结果是: %d/n", count);
        return 0;
    }

    运行结果是:

    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进阶知识

     

    先看一份代码:

    /*
    *  fork_test.c
    *  version 2
    *  Created on: 2010-5-29
    *      Author: wangth
    */
    
    #include <unistd.h>
    #include <stdio.h>
    
    int main(void)
    {
        int i = 0;
        printf("i son/pa ppid pid  fpid/n");
        //ppid指当前进程的父进程pid
        //pid指当前进程的pid,
        //fpid指fork返回给当前进程的值
        for(i = 0; i < 2; i++)
        {
            pid_t fpid = fork();
            if(fpid == 0)
                printf("%d child  %4d %4d %4d/n", i, getppid(), getpid(), fpid);
            else
                printf("%d parent %4d %4d %4d/n", i, getppid(), getpid(), fpid);
        }
        return 0;
    }

    运行结果是:

    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),代码内容为:

    for(i = 0; i < 2; i++)
    {
        pid_t fpid = fork(); //执行完毕,i=0,fpid=3225
        if(fpid == 0)
            printf("%d child  %4d %4d %4d/n", i, getppid(), getpid(), fpid);
        else
            printf("%d parent %4d %4d %4d/n", i, getppid(), getpid(), fpid);
    }
    return 0;

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

    for(i = 0; i < 2; i++)
    {
        pid_t fpid = fork(); //执行完毕,i=0,fpid=0
        if(fpid == 0)
            printf("%d child  %4d %4d %4d/n", i, getppid(), getpid(), fpid);
        else
            printf("%d parent %4d %4d %4d/n", i, getppid(), getpid(), fpid);
    }
    return 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的父进程。父子是相对的,这个大家应该容易理解。只要当前进程执行了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()函数。

    我们再来看一份代码:

    /*
    *  fork_test.c
    *  version 3
    *  Created on: 2010-5-29
    *      Author: wangth
    */
    
    #include <unistd.h>
    #include <stdio.h>
    
    int main(void)
    {
        int i = 0;
        for(i = 0; i < 3; i++)
        {
            pid_t fpid = fork();
            if(fpid == 0)
                printf("son/n");
            else
                printf("father/n");
        }
        return 0;
    
    }

    它的执行结果是:

    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_test.c
    *  version 4
    *  Created on: 2010-5-29
    *      Author: wangth
    */
    
    #include <unistd.h>
    #include <stdio.h>
    
    int main()
    {
        pid_t fpid;//fpid表示fork函数返回的值
        //printf("fork!");
        printf("fork!/n");
        fpid = fork();
        if (fpid < 0)
            printf("error in fork!");
        else if (fpid == 0)
            printf("I am the child process, my process id is %d/n", getpid());
        else
            printf("I am the parent process, my process id is %d/n", getpid());
        return 0;
    }

    执行结果如下:

    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函数。

    #include <stdio.h>
    #include <unistd.h>
    
    int main(int argc, char *argv[])
    {
        fork();
        fork() &&fork() || fork();
        fork();
        return 0;
    }

    问题是不算main这个进程自身,程序到底创建了多少个进程。

    为了解答这个问题,我们先做一下弊,先用程序验证一下,到此有多少个进程。

    #include <stdio.h>
    
    int main(int argc, char *argv[])
    {
        fork();
        fork() &&fork() || fork();
        fork();
        printf("+/n");
    }

    答案是总共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/jason314/article/details/5640969

     

     

    展开全文
  • php中pcntl_fork详解

    万次阅读 2020-06-19 14:38:34
    ** pcntl_fork()函数是php-pcntl模块中用于创建进程的函数。(不支持windows) ...当:pcntl_fork()函数执行的时候,会创建一个子进程。子进程会复制当前进程,也就是父进程的所有:数据,代码,还有状态。

    pcntl_fork()函数是php-pcntl模块中用于创建进程的函数。(不支持windows)

    至于php_pcntl扩展如何安装开启这里就不介绍了,只分析pcntl_fork()这个函数本身。

    $one = 123;
    $one++;
    $two = time();
    $pid = [];
    $pid = pcntl_fork();
    $three = time();
    

    当:pcntl_fork()函数执行的时候,会创建一个子进程。子进程会复制当前进程,也就是父进程的所有:数据,代码,还有状态。

    1.当pcntl_fork()创建子进程成功后,在父进程内,返回子进程号,在子进程内返回0,失败则返回-1

    2.子进程会复制父进程的代码,数据。那么就说明:子,父进程拥有的代码和数据会一模一样。

    3.重点:子进程会复制父进程的状态,那么就有上面的示例代码:在第五行执行了pcntl_fork,那么创建出的子进程,代码也是从第五行开始执行的。又子进程复制了数据,代码。所以,在子进程内同理存在:$one,$two等变量

    for ($i = 0; $i < 3; $i++) {
        $pid = pcntl_fork();
    }
    sleep(30);
    

    那么:上面的for循环,实际会产生多少个子进程?答案是7个,在linux下,用ps命令将可以看到8个进程(1个父进程,7个子进程)
    原因:父进程在 i = 0 时 , 创 建 出 一 个 子 进 程 0 , 此 时 的 子 进 程 , 还 会 继 续 执 行 循 环 。 创 建 出 属 于 自 己 的 子 进 程 。 同 理 : i=0时,创建出一个子进程0,此时的子进程,还会继续执行循环。创建出属于自己的子进程。同理: i=00i=1时也会这样……

    展开全文
  • Go fork是一个为Go提供类似fork的仿真的程序包。实际上,它不是非常像fork的,但是提供了fork()用于的常见功能,即使子例程(goroutine)在新的进程空间中运行。 就其本机线程性质而言,Go是不安全的。这个pkg并没有...
  • pcntl_fork()函数就是为当前的进程创建一个子进程。并且先运行父进程,返回的是子进程的PID,肯定大于零。在父进程的代码中可以用pcntl_fork(&$status)暂停父进程知道他的子进程有返回值。注意:父进程的阻塞...
  • Fork Program

    2015-02-05 22:46:58
    Program on fork() system call to understand its functionality
  • fork && fork || fork问题

    2017-08-10 10:05:11
    #include #include int main() { fork();/*****/ fork() && fork() || fork();/*****/ fork();/*****/ sleep(100); return 0; 问题是
  • Fork for mac

    2019-12-27 17:09:01
    fork, 一款 最好用的 git 客户端 程序 ;官方最新版. mac
  • fork函数使用

    2017-10-23 17:57:35
    关于fork函数的介绍,和测试程序,原版fork linux平台
  • fork()编程fork()编程fork()编程fork()编程fork()编程fork()编程fork()编程
  • fork cms 3.1.7

    2021-03-24 11:09:44
    ForkCMS使用十分简单,注重用户体验,包含博客、问答、表格等模块,适合于建立小型商业网站或者个人博客,支持中文,安装的第二步请选择中文即可! ForkCMS3.1.0升级TinyMCE到3.4.7版本,该编辑器支持所有的语言;改进...
  • fork工具mac版本

    2018-05-29 09:43:34
    fork工具mac版本fork工具mac版本fork工具mac版本fork工具mac版本
  • linux下fork指令

    千次阅读 2018-10-20 18:42:22
    fork
  • Fork-It 是一个 NodeJs 模块,用于处理进程间通信的复杂性。 该库将抽象在两个组件之间发送流程消息的复杂性。 它还在两个进程之间创建了一个本质上非常 RPC 的通信层。 状态:BETA(不稳定) 例如: 简单的父/子...
  • fork2-node-greet fork2-mini-harp 中间件机制 测试用例: fork2-myexpress 实现自己的express 了解express原理机制 测试用例:
  • fork() || fork()和fork() && fork()笔试题

    千次阅读 2017-02-12 23:17:11
    程序一:int main() { fork() || fork(); printf("+"); return 0; } 结果输出3个”+”,共创建3个进程。...在main这个主进程中,首先执行 fork() || fork(), 左边的fork()返回一个非零值,根据||的短路
  • Linux fork隐藏的开销-过时的fork(正传)

    千次阅读 多人点赞 2019-09-03 23:20:56
    本文来自《Linux fork那些隐藏的开销》 fork是一个拥有50年历史的陈年系统调用,它是一个传奇!时至今日,它依旧灿烂。 一个程序员可以永远不用read/write,也可以不懂mmap,但必须懂fork。这是一种格调! fork没有...
  • cfork, 群集 fork 并重新启动简单路径 cfork 群集 fork 并重新启动简单路径。具有工作文件路径的简单 fork处理工人重新启动,即使是意外退出。自动错误日志进程 uncaughtException 事件安装$ npm ins
  • Fork Gradle插件 现在,它已作为子模块移入了主项目。
  • Linux fork面试题

    千次阅读 2015-09-06 09:54:17
    fork
  • git客户端fork安装包

    2021-02-04 19:30:40
    fork 是一个管理git代码的一个可视化客户端
  • linux中fork()函数详解(原创!!实例讲解)

    万次阅读 多人点赞 2010-06-01 23:35:00
    一、fork入门知识 二、fork进阶知识 三、fork高阶知识
  • fork3()编程fork3()编程fork3()编程fork3()编程fork3()编程fork3()编程fork3()编程

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 295,550
精华内容 118,220
关键字:

fork