关于父进程和子进程关系,以及fork()进程数量

父进程和子进程关系:子进程拥有父进程几乎所有资源的拷贝,重新复制存储空间,包括标准输入,输出的缓冲空间以及程序。当程序中出现fork后,子进程将从fork语句开始复制程序代码,并且在子进程中执行。关于fork()执行后,在子进程中返回值为0,在父进程中返回值为整数即子进程的ID。

1)循环fork()N次,产生多少个子进程

/* 
 *  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;  
  
}  

  根据以上程序可知,产生的子进程数量为:1+2+4+……+2N-1

     执行printf的打印次数为:2*(1+2+4+……+2N-1)次

2)子进程会复制父进程的输入输出缓冲区,如果父进程的输入输出缓冲区没有输出的话,子进程会复制

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;  
}  

3)以下fork()连续中会产生多少个新的子进程

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

  

如果有一个这样的表达式:cond1 && cond2 || cond3 这句代码会怎样执行呢?


1、cond1为假,那就不判断cond2了,接着判断cond3
2、cond1为真,这又要分为两种情况:
  a、cond2为真,这就不需要判断cond3了
  b、cond2为假,那还得判断cond3


fork调用的一个奇妙之处在于它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
1、在父进程中,fork返回新创建子进程的进程ID;
2、在子进程中,fork返回0;
3、如果出现错误,fork返回一个负值(题干中说明了不用考虑这种情况)


在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。


有了上面的知识之后,下面我们来分析fork() && fork() || fork()会创建几个新进程

很明显fork() && fork() || fork()创建了4个新进程

总结:

第一注释行的fork生成1个新进程
第二注释行的三个fork生成4+4=8个新进程
第三注释行的ork会生成10个新进程(这是因为前面总共有10个进程,调用一次fork生成10个新进程)

所以一共会生成1+8+10=19个新进程

4)父进程和子进程对文件表项的共享,子进程会复制父进程所有打开的文件描述符,也就是对复制的文件描述符共享同一个文件表项。即对一个文件共享同一个位移量的等。

fork之后对文件描述符的处理:父进程等待子进程处理完毕;父,子进程各自执行不同的程序段,fork之后,各自关闭它们不需要使用的文件描述符,比如socket编程中的描述符。

 

5)了解fork的内存复制机制,关于这个,我们只需要了解一句话就够了,“子进程复制父进程的数据空间(数据段)、栈和堆,父、子进程共享正文段。”也就是说,对于程序中的数据,子进程要复制一份,但是对于指令,子进程并不复制而是和父进程共享。具体来看下面这段代码(这是我在上面那段代码上稍微添加了一点东西):

#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<stdarg.h>
#include<errno.h>
#define BUFSIZE 512
#define LEN 2
void err_exit(char *fmt,...);
int main(int argc,char *argv[])
{
    pid_t pid;
    int loop;

    for(loop=0;loop<LEN;loop++)
    {
    printf("Now is No.%d loop:\n",loop);

    if((pid=fork()) < 0)
        err_exit("[fork:%d]: ",loop);
    else if(pid == 0)
    {
      printf("[Child process]P:%d C:%d\n",getpid(),getppid()); 
    }
    else
    {
        sleep(5);
    }
    }

    return 0;
}

 



为什么上面那段代码会创建三个子进程?我们来具体分析一下它的执行过程:

首先父进程执行循环,通过fork创建一个子进程,然后sleep5秒。

再来看父进程创建的这个子进程,这里我们记为子进程1.子进程1完全复制了这个父进程的数据部分,但是需要注意的是它的正文段是和父进程共享的。也就是说,子进程1开始执行代码的部分并不是从main的 { 开始执行的,而是主函数执行到哪里了,它就接着执行,具体而言就是它会执行fork后面的代码。所以子进程1首先会打印出它的ID和它的父进程的ID。然后继续第二遍循环,然后这个子进程1再来创建一个子进程,我们记为子进程11,子进程1开始sleep。

子进程11接着子进程1执行的代码开始执行(即fork后面),它也是打印出它的ID和父进程ID(子进程1),然后此时loop的值再加1就等于2了,所以子进程2直接就返回了。

那个子进程1sleep完了之后也是loop的值加1之后变成了2,所以子进程1也返回了!

然后我们再返回去看父进程,它仅仅循环了一次,sleep完之后再来进行第二次循环,这次又创建了一个子进程我们记为子进程2。然后父进程开始sleep,sleep完了之后也结束了。

那么那个子进程2怎么样了呢?它从fork后开始执行,此时loop等于1,它打印完它的ID和父进程ID之后,就结束循环了,整个子进程2就直接结束了!

posted on 2015-08-01 13:34  菜鸟基地  阅读(996)  评论(0)    收藏  举报

导航