一道面试题:关于fork的问题

FROM 酷壳:http://coolshell.cn/articles/7965.html

     前两天有人问了个关于Unix的fork()系统调用的面试题,这个题正好是我大约十年前找工作时某公司问我的一个题,我觉得比较有趣,写篇文章与大家分享一下。这个题是这样的:

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

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
   int i;
   for(i=0; i<2; i++){
      fork();
      printf("-");
   }

   return 0;
}

如果你对fork()的机制比较熟悉的话,这个题并不难,输出应该是6个“-”,但是,实际上这个程序会很tricky地输出8个“-”。

要讲清这个题,我们首先需要知道fork()系统调用的特性,

  • fork()系统调用是Unix下以自身进程创建子进程的系统调用,一次调用,两次返回,如果返回是0,则是子进程,如果返回值>0,则是父进程(返回值是子进程的pid),这是众为周知的。
  • 还有一个很重要的东西是,在fork()的调用处,整个父进程空间会原模原样地复制到子进程中,包括指令,变量值,程序调用栈,环境变量,缓冲区,等等。

所以,上面的那个程序为什么会输入8个“-”,这是因为printf(“-”);语句有buffer,所以,对于上述程序,printf(“-”);把“-”放到了缓存中,并没有真正的输出(参看《C语言的迷题》中的第一题),在fork的时候,缓存被复制到了子进程空间,所以,就多了两个,就成了8个,而不是6个。

另外,多说一下,我们知道,Unix下的设备有“块设备”和“字符设备”的概念,所谓块设备,就是以一块一块的数据存取的设备,字符设备是一次存取一个字符的设备。磁盘、内存都是块设备,字符设备如键盘和串口。块设备一般都有缓存,而字符设备一般都没有缓存

对于上面的问题,我们如果修改一下上面的printf的那条语句为:

printf("-\n");

或是

printf("-");
fflush(stdout);

就没有问题了(就是6个“-”了),因为程序遇到“\n”,或是EOF,或是缓中区满,或是文件描述符关闭,或是主动flush,或是程序退出,就会把数据刷出缓冲区。需要注意的是,标准输出是行缓冲,所以遇到“\n”的时候会刷出缓冲区,但对于磁盘这个块设备来说,“\n”并不会引起缓冲区刷出的动作,那是全缓冲,你可以使用setvbuf来设置缓冲区大小,或是用fflush刷缓存。

我估计有些朋友可能对于fork()还不是很了解,那么我们把上面的程序改成下面这样:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
   int i;
   for(i=0; i<2; i++){
      fork();
      //注意:下面的printf有“\n”
      printf("ppid=%d, pid=%d, i=%d \n", getppid(), getpid(), i);
   }
   sleep(10); //让进程停留十秒,这样我们可以用pstree查看一下进程树
   return 0;
}

于是,上面这段程序会输出下面的结果,(注:编译出的可执行的程序名为fork)

ppid=8858, pid=8518, i=0
ppid=8858, pid=8518, i=1
ppid=8518, pid=8519, i=0
ppid=8518, pid=8519, i=1
ppid=8518, pid=8520, i=1
ppid=8519, pid=8521, i=1

$ pstree -p | grep fork
|-bash(8858)-+-fork(8518)-+-fork(8519)---fork(8521)
|            |            `-fork(8520)

面对这样的图你可能还是看不懂,没事,我好事做到底,画个图给你看看:

注意:上图中的我用了几个色彩,相同颜色的是同一个进程。于是,我们的pstree的图示就可以成为下面这个样子:(下图中的颜色与上图对应)

这样,对于printf(“-”);这个语句,我们就可以很清楚的知道,哪个子进程复制了父进程标准输出缓中区里的的内容,而导致了多次输出了。(如下图所示,就是我阴影并双边框了那两个子进程)

现在你明白了吧。(另,对于图中的我本人拙劣的配色,请见谅!)

时间: 2024-07-28 16:50:44

一道面试题:关于fork的问题的相关文章

一道面试题(关于千万量级数据结构排序)

问题描述 一道面试题(关于千万量级数据结构排序) 题目: 已知文件中存有全国英语六级历年来的成绩(千万级别,考生分数都是正整数,最高710分),每一行都是一个人的姓名.考号和成绩,请你对考生的成绩从高到低进行排序,输出到另一个文件中. 格式 如下: 李四,201008823,678: 张三,201007432,356: 王五,201322233,464: 排序后: 李四,201008823,678: 王五,201322233,464: 王五,201322233,464: 要求:使空间复杂度和时间

语言 面试题-一道面试题,不是很清楚这个例子怎么解答,求大神帮助.

问题描述 一道面试题,不是很清楚这个例子怎么解答,求大神帮助. 提问是 这段代码有什么问题, 有什么解决思路.(我其实连问题都没看出来,代码可以编译) // Memory-mapped peripheral#define STATUS_REG_ADDR 0x12345678 // 32-bit status register#define DATA_REG_ADDR 0x1234567C // 32-bit data register // Status register bits#define

初始化顺序-今年阿里巴巴的一道笔试题

问题描述 今年阿里巴巴的一道笔试题 public class Test1 { public static int k = 0; public static Test1 t1 = new Test1("t1"); public static Test1 t2 = new Test1("t2"); public static int i = print("i"); public static int n = 99; public int j = pr

结构体定义-如何定义满足以下的Node与List结构体,今天参加斐讯的一道笔试题。

问题描述 如何定义满足以下的Node与List结构体,今天参加斐讯的一道笔试题. Node包含50个字符.

从一道面试题说去

    有一道面试题: 给定n个整型数,怎样让这n个数的使用空间最小.      ok,我们都知道在32位的机器下,int类型的数占4个字节,因此n个数总的使用空间应该是4n.(64位不做解释)那我们怎么样才能使得n个数字的使用空间最小呢?     一. 我们先来看一个例子           假设现在有3个数,1,2,3.           我们都知道数字最后都是以二进制的方式存储的,我们可以表示出1,2,3的二进制           1: 0000 0000 0000 0000 0000

《Wireshark网络分析就这么简单》—从一道面试题开始说起

从一道面试题开始说起Wireshark网络分析就这么简单从一道面试题开始说起我每次当面试官,都要伪装成无所不知的大牛. 这当然是无奈的选择--现在每封简历都那么耀眼,不装一下简直镇不住场面.比如尚未毕业的本科生,早就拿下CCIE认证:留欧两年的海归,已然精通英.法.德三门外语:最厉害的一位应聘者,研究生阶段就在国际上首次提出了计算机和生物学的跨界理论--可怜我这个老实人在一开场还能装装,到了技术环节就忍不住提问基础知识,一下子把气氛从学术殿堂拉到建筑工地.不过就是这些最基础的问题,却常常把简历精

《大咖讲Wireshark网络分析》—从一道面试题开始说起

从一道面试题开始说起大咖讲Wireshark网络分析我每次当面试官,都要伪装成无所不知的大牛. 这当然是无奈的选择--现在每封简历都那么耀眼,不装一下简直镇不住场面.比如尚未毕业的本科生,早就拿下CCIE认证:留欧两年的海归,已然精通英.法.德三门外语:最厉害的一位应聘者,研究生阶段就在国际上首次提出了计算机和生物学的跨界理论--可怜我这个老实人在一开场还能装装,到了技术环节就忍不住提问基础知识,一下子把气氛从学术殿堂拉到建筑工地.不过就是这些最基础的问题,却常常把简历精英们难住.本文要介绍的便

一道面试题:布尔变量

FROM:酷壳 下面这篇文章是从StackOverflow来的.LZ面试的时候遇到了一道面试题:"如果有三个Bool型变量,请写出一程序得知其中有2个以上变量的值是true",于是LZ做了下面的这样的程序: boolean atLeastTwo(boolean a, boolean b, boolean c) { if ((a && b) || (b && c) || (a && c)) { return true; } else { r

mysql一道面试题

mysql> #一道面试题 mysql> #把一张表num 的值[20-30]之间的数全改为20 mysql> #并且把[30-40]之间的数全改为30 mysql> create table mianshi ( -> num int -> ); Query OK, 0 rows affected (1.94 sec) mysql> show tables; +----------------+ | Tables_in_test | +--------------