关于父进程和子进程关系,以及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就直接结束了!
浙公网安备 33010602011771号