天天看点

当fork遇见for循环,printf后会怎样?

这段时间总看见这种题,今天总结一下。

题1

先看代码1:

#include<unistd.h>
#include<stdio.h>
#include<sys/types.h>
int main()
{
    printf("1 2 3");
    fork();
}      

代码2:

#include<unistd.h>
#include<stdio.h>
#include<sys/types.h>
int main()
{
    printf("1 2 3\n");
    fork();
}      

以上两段代码基本一样,不一样的就是‘\n’,我们自然能想到缓冲区的问题。

printf

这条命令处理起来却应该分为两步:

  1. 系统将printf()中传进的数据写进进程的缓冲区
  2. 对于标准输出而言(行缓冲),系统判断如果缓冲区中有“\n”,或是EOF,或是主动刷出,或是缓冲区满,或是文件描述符关闭,或是exit,就会把数据从缓冲区中输出出来(flush)

也就是说对于printf()代表的行缓冲而言,数据只是先被存储在了缓冲区,直到碰见上述情况才会输出。如果缓冲区内有内容,当进程退出时(exit)也会自动进行输出。

我们再来看这两段代码,我们先看一下结果:

代码1:

当fork遇见for循环,printf后会怎样?

代码2:

当fork遇见for循环,printf后会怎样?

结果分析

代码1中主进程的缓冲区因为执行时没有输出条件,所以内容被子进程继承,两个进程结束时触发exit,输出缓冲区内容,结果为

1 2 3 1 2 3

而代码2因为执行时有’\n’条件所以主进程输出1 2 3后缓冲区刷空,子进程继承来空的buffer自然无输出,结果为

1 2 3

题2

基于题一的知识,我们看题2。

代码1:

#include<unistd.h>
#include<stdio.h>
#include<sys/types.h>
int main()
{
    int i=0;
    for(i=0;i<2;i++)           
    {
        fork();
        printf("-");
    }
    return 0;
}      

代码2:

#include<unistd.h>
#include<stdio.h>
#include<sys/types.h>
int main()
{
    int i=0;
    for(i=0;i<2;i++)           
    {
        fork();
        printf("-\n");
    }
    return 0;
}      

结果:

代码1:

当fork遇见for循环,printf后会怎样?

代码2:

当fork遇见for循环,printf后会怎样?

基于题一的解释,应该各输出几个-呢?

题中,有两层循环:

(1)一次循环后有两个进程,

代码1中主进程的缓冲区因为执行时没有输出条件,所以内容被子进程继承,此时两个进程的缓冲区各有一个’-’ 。

代码2中因为有执行条件’\n’,所以主进程和子进程各打印出一个’-’

(2)第二次循环后,两个进程分别fork出各自的子进程,所以共四个进程,

代码1中,同理,fork出的子进程继承父进程的缓冲区,此时四个进程的缓冲区都有’-‘,再次执行printf,各自的缓冲区都多了一个’-‘,结束时触发exit,输出缓冲区内容,结果为8个。

而代码2中,四个进程缓冲区为空,执行printf,打印四个,共6个。

题三

看代码,共有多少个进程数?

#include<stdio.h>
int main()
{
  fork();
  fork()&&fork()||fork();
  fork();
}      

我们需要知道几个问题:

  1. 父进程fork出子进程,子进程从fork后面那个指令开始执行的
  2. fork后父进程返回子进程PID,子进程返回0
  3. 对于”a&&b”表达式,如果a为0,b就不会执行

(1)第一次fork,进程A变成A和其子进程B

当fork遇见for循环,printf后会怎样?

(2)fork()&&fork()||fork();分析如下图:

当fork遇见for循环,printf后会怎样?

注意:

C和D都是子进程,那么它俩返回的值都是0,因此”&&”后面的表达式就不会执行,之后执行最后一个fork()函数。

但是A和B两个父进程返回值大于0,因此会执行后面的”fork()||fork()”

(3)最后一次fork