【Linux】请问下面的程序一共输出多少个“-”?

请问下面的程序一共输出多少个“-”?

1
2
3
4
5
6
7
8
9
int main(void)
{
int i;
for (i = 0; i < 2; i++) {
fork();
printf("-");
}
return 0;
}

运行结果

1
2
[misaki@localhost test]$ ./main 
--------

结果分析

  为啥会有8个’-‘呢?我们把这个程序稍微修改一下再看下结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <unistd.h>
int main(void)
{
int i;
for (i = 0; i < 2; i++)
{
fork();
printf("-");
fflush(stdout);
}
sleep(1);
return 0;
}



[misaki@localhost test]$ ./main
------

  现在又变成了6个’-‘,我们先从以上这段程序开始分析。我们分别打印各个进程的pid和他们的ppid来让我们看的更清楚一些,并且查看以下进程树。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <stdio.h>
#include <unistd.h>
int main(void)
{
int i;
for (i = 0; i < 2; i++)
{
fork();
printf("ppid:%d, pid:%d\n", getppid(), getpid());
fflush(stdout);
}
sleep(10);
return 0;
}


[misaki@localhost test]$ ./main
ppid:2221, pid:4846
ppid:4846, pid:4847
ppid:2221, pid:4846
ppid:4846, pid:4848
ppid:4846, pid:4847
ppid:4847, pid:4849


[misaki@localhost ~]$ pstree -ap misaki
sshd,2220
├─bash,2221
│ └─main,4846
│ ├─main,4847
│ │ └─main,4849
│ └─main,4848
└─bash,3571
└─pstree,4850 -ap misaki

  fork()执行后子进程会赋值父进程的代码段,以及PCB中的部分数据其中包括程序计数器,上下文数据等来保证自己会按照父进程当前的执行流和代码继续执行下去。
  我们的父进程4846进入循环后首先创建了子进程4847然后打印一次,然后再次执行一次循环有创建了一个子进程4848然后又打印一次,至此父进程执行完毕循环进入sleep(),于是父进程会打印两次。
  子进程4847在创建后先打印一次,然后继续执行流再次执行循环又会创建一个子进程4849,然后再打印一次,随后循环结束,于是子进程4847也会打印两次。
  子进程4848是在父进程4846第二次循环创建出来的,于是只打印一次退出循环,子进程4848只会打印一次。
  孙子进程4849是在子进程4847第二次循环创建出来的,于是也只打印一次便结束了循环,子进程4849也只打印一次。
  至此一共打印六次全部完毕。
  我们回到最初的问题,那么这道题为什么会是8次,而我每次打印后刷新一次缓冲区结果就变成了6次.
  其实我们的子进程会复制父进程的缓冲区。关于缓冲区,Unix下的设备块设备字符设备的概念,所谓块设备,就是以一块一块的数据存取的设备,字符设备是一次存取一个字符的设备。磁盘、内存都是块设备,字符设备如键盘和串口。块设备一般都有缓存,而字符设备一般都没有缓存。
  程序遇到“\n”,或是EOF,或是缓冲区满,或是文件描述符关闭,或是主动flush,或是程序退出,就会把数据刷出缓冲区。需要注意的是,标准输出是行缓冲,所以遇到“\n”的时候会刷出缓冲区,但对于磁盘这个块设备来说,“\n”并不会引起缓冲区刷出的动作,那是全缓冲,你可以使用setvbuf来设置缓冲区大小,或是用fflush刷缓存。
  我们的子进程在创建时复制了父进程的缓冲区,而此时的父进程的标准输入的缓冲区并没有刷新,就会导致在printf后面创建的子进程复制的缓冲区中还存有父进程打印的’-‘,以上的例子中的48484849这两个进程如果在没有刷新缓冲区的情况下就会复制上父进程输出的’-‘导致多打出两个’-‘,于是这样就解释的通为什么会打印8个了。
  这道题还是有一点坑的,需要对fork()创建子进程的底层实现有清楚的认识和了解,也需要对缓冲区相关的知识有一定了解。

-------------本文结束感谢您的阅读!-------------
记录学习每一分,感谢您的赞助