Linux 中的 fork(卡塔尔(قطر‎ 函数实例深入分析

只有一个进程在执行这段代码,将要执行的下一条语句都是if(fpid&lt

图片 4

二、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高阶知识”讲。

总括一下,这些程序推行的流水生产线如下:

图片 1

其风流倜傥顺序最后发生了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

这里就不做详细表达了,只做三个大概的深入分析。

图片 2

里面每生龙活虎行分别代表一个历程的周转打字与印刷结果。

计算一下法规,对于这种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)个进程,这一个说法是不没有错,希望我们供给注意。

数学推理见(该博文的末尾)。

再者,大家若是想测一下一个主次中到底创制了多少个子进度,最佳的诀要正是调用printf函数打字与印刷该进度的pid,也即调用printf(“%d/n”,getpid(卡塔尔State of Qatar;恐怕通过printf(“+/n”State of Qatar;来判断产生了几个经过。有人想透过调用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(“+”State of Qatar;不能正确地反应进度的数目。

大家看了这样多大概有一点点疲倦吧,可是笔者还得贴最终后生可畏份代码来更为剖判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");  
}

答案是一同十几个经过,除去main进度,还也许有二十个进程。

小编们再来留心剖判一下,为啥是还恐怕有十多个进度。
第多个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个支行。

图片 3

增加前边的fork和终极的fork,总共4*5=十柒个经过,除去main主进度,正是18个进程了。

后生可畏、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(State of Qatar早前,只有叁个历程在推行这段代码,但在此条语句之后,就造成多少个经过在实施了,那三个进程的大概完全相通,就要实行的下一条语句都以if(fpid<0State of Qatar……

缘何五个进程的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施行落成后,现身七个进度,

图片 4

有一些人会讲八个经过的内容完全等同啊,怎么打字与印刷的结果不一致等啊,那是因为推断规范的缘故,上边列举的只是经过的代码和指令,还大概有变量啊

进行完fork后,进度1的变量为count=0,fpid!=0(父进度)。进度2的变量为count=0,fpid=0(子进程),那多个经过的变量都以单独的,存在分歧的地点中,不是公家的,这一点要留意。能够说,大家即使经过fpid来鉴定分别和操作父子进度的。

还大概有人也许思疑为何不是从#include处在此以前复制代码的,那是因为fork是把经过近来之处拷贝生机勃勃份,实行fork时,进度生机勃勃度试行完了int
count=0;fork只拷贝下八个要试行的代码到新的进程。

三、fork高阶知识

这一块笔者根本就fork函数讲一下操作系统进度的创始、命丧黄泉和调治等。因为时间和活力节制,作者先写到这里,后一次找个日子本身争取把剩下的内容补齐。