fork

引言

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

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

#include <iostream>
#include <unistd.h>  

using namespace std;

int main(){

    pid_t fpid = fork();

    int cnt = 0;
    if (fpid < 0){
        cout << "开启进程失败! "<< endl;
    }else if(fpid == 0){
        cout << endl << "子进程 pid : " << getpid() << endl;
        cnt++;
        cout << "cnt = " << cnt << endl;
    }
    else{
        cout << endl << "父进程 pid : " << getpid() << endl;
        cnt++;
        cout << "cnt = " << cnt << endl;
    }
    cout << "Finally, 进程 pid : " << getpid();
    cout << ", cnt = " << cnt << endl;

    return 0;
}
父进程 pid : 3500
cnt = 1
Finally, 进程 pid : 3500, cnt = 1

子进程 pid : 3501
cnt = 1
Finally, 进程 pid : 3501, cnt = 1

如何判断父/子线程?

pid_t fpid = fork()会有返回值,会返回两次,通过判断fpid的值,可以知道是父/子线程。

  • fpid<0
    线程开启失败

  • fpid==0
    子线程

  • 其他(实际上是子进程的pid)
    父进程

要知道:fpid只是一个判断的标志,但是值得注意的是pid与fork返回的fpid就像链表一样,连接着父/子进程,子进程没有创建其他进程,所以子进程的fpid==0,如上例中,

fork进阶

#include <iostream>
#include <unistd.h>  
#include <stdio.h>
using namespace std;
int main()
{
    int i = 0;

    // ppid指当前进程的父进程pid  
    // pid指当前进程的pid,  
    // fpid指fork返回给当前进程的值  

    cout << "i\tson/pa \tppid\tpid\tfpid" << endl;

    for (i = 0; i<2; i++){
        pid_t fpid = fork();
        if (fpid == 0)
            printf("%d \tchild \t%4d \t%4d \t%4d\n", i, getppid(), getpid(), fpid);
        else
            printf("%d \tparent \t%4d \t%4d \t%4d\n", i, getppid(), getpid(), fpid);
    }
    return 0;
}
i   son/pa  ppid    pid     fpid
0   parent  3176    3604    3605
1   parent  3176    3604    3606
1   child   1252    3606       0
0   child   1252    3605       0
1   parent  1252    3605    3607
1   child   1252    3607       0

上面的结果什么意思?且看下图,

再看一份代码:

#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
father
father
son
son
father
son
father
father
son
son
son
father
son

实际上和上面那个图有点类似,简单来说就是一次fork就会产生一个parent和一个child,产生的parent和当前进程的pid是一样的,子进程则是在这个基础上加上一个偏移。

        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++2N1 次,创建的子进程数为 1+2+4++2N1 个.数学推理见http://202.117.3.13/wordpress/?p=81(该博文的最后)。

同时,大家如果想测一下一个程序中到底创建了几个子进程,最好的方法就是调用printf函数打印该进程的pid,也即调用printf(“%d\n”,getpid());或者通过printf(“+\n”);来判断产生了几个进程。有人想通过调用printf(“+”);来统计创建了几个进程,这是不妥当的。具体原因我来分析。

老规矩,大家看一下下面的代码:

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

fork了多少次

#include <stdio.h> 
#include <unistd.h>

int main(int argc, char* argv[])  
{  
   fork();  
   fork() && fork() || fork();  
   fork();  
   printf("+\n");  
}

运行结果为20次,还有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个进程了。

参考

http://blog.csdn.net/jason314/article/details/5640969

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值