一个fork的面试题

标签: C/C++语言 Unix/Linux 操作系统 编程语言 趣味问题 | 发表时间:2012-08-01 08:20 | 作者:陈皓
出处:http://coolshell.cn

前两天有人问了个关于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(“-”);语句,我们知道,Unix下的设备有“ 块设备”和“ 字符设备”的概念,所谓块设备,就是以一块一块的数据存取的设备,字符设备是一次存取一个字符的设备。磁盘、内存、显示器都是块设备,字符设备如键盘和串口。 块设备一般都有缓存,而字符设备一般都没有缓存

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

我们如果修改一下上面的printf的那条语句为:

printf("-\n");

或是

 printf("-");
flush();

就没有问题了,因为程序遇到“\n”或是EOF,或是缓中区满,或是文件描述符关闭,或是主动flush,就会把数据刷出缓冲区。

我估计有些朋友可能对于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 | grep fork
|-bash(8858)-+-fork(8518)-+-fork(8519)---fork(8521)
|            |            `-fork(8520)

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

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

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

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

(全文完)

(转载本站文章请注明作者和出处 酷壳 – CoolShell.cn ,请勿用于任何商业用途)

————————============ 感谢 42qu.com 为本站提供 VPS ============————————
您可能也喜欢:

“火柴棍式”程序员面试题

面试题:赛马问题

Fork 系统炸弹

到处都是Unix的胎记

140个Google的面试题
无觅

相关文章

相关 [fork 面试] 推荐:

一个fork的面试题

- - 酷壳 - CoolShell.cn
前两天有人问了个关于Unix的fork()系统调用的面试题,这个题正好是我大约十年前找工作时某公司问我的一个题,我觉得比较有趣,写篇文章与大家分享一下. 题目:请问下面的程序一共输出多少个“_”. 如果你对fork()的机制比较熟悉的话,这个题并不难,输出应该是6个“-”,但是,实际上这个程序会很tricky地输出8个“-”.

叉子蜡烛:Candle Fork

- 阳阳 - 爱…稀奇~{新鲜:科技:创意:有趣}
来自设计师Rhea Jeong的创意,叉子蜡烛(Candle Fork)会是生日聚会时的不错装备,一头是蜡烛,一头是叉子,点着蜡烛唱歌,吹熄蜡烛吃蛋糕(中间还有接头,可以把燃过的蜡烛头去掉),人生得意须尽欢啊~. 设计师:Rhea Jeong. 亲爱的,这些东西也会对你胃口:. 1小时蜡烛:1 Hour Candle.

不仅是微软和诺基亚,谁都无法 fork Android,因为它就没法 fork

- - 博客 - 伯乐在线
虽然是名义上的开源系统,但如果微软的手机采用 Android 系统,那将是个巨大的错误,诺基亚都不行,因为 Google 把 Android 做得无人可改. 不止一次了,总有人跳出来「建议」微软采用 Android,替换掉市场乏力的 Windows Phone 系统. 说这话的人到底是人笨呢,还是心眼坏.

如何正确地使用vfork():简析vfork()与fork()的不同

- - CSDN博客编程语言推荐文章
今天看到知乎上有人问了一个 由于不恰当的使用vfork()而导致的一个奇怪现象,底下的回答非常精彩. 趁此机会我也仔细了解了一下vfork()的特性. 其实对vfork()最完备、权威的表述莫过于man手册里面的解释了. 简单的说,vfork()跟fork()类似,都是创建一个子进程,这两个函数的的返回值也具有相同的含义.

如何在github上fork一个项目来贡献代码以及同步原作者的修改

- - 博客园_Ruby's Louvre
作为一个IT人,通过github进行学习是最快的成长手段. 但只看不动手还是成长得很慢,因此为别人贡南代码才是明智之举. 比如我们看下片看,许多大片都是由字幕组免费翻译压制的. 因为他们都是聪明的大学生,为了提高听力水平,提高笔译水平才这样干的. 因此贡献代码,参与开源项目都是有益无害的. 首先你总得有 自己的github帐号吧,注册一个,非常简单,只需用户名,邮箱,密码,邮箱只是用来找回密码的,不做验证.

Redis BGSAVE因为内存不足 fork 失败导致目标 Redis 无法访问的问题 - piperck - 博客园

- -
中秋的时候正在外面愉快的在外卖喝着咖啡玩电脑. 突发 redis 报警从 sentry 应用端曝出的错误. 于是又开始愉快的处理问题了,看上去像是执行 rdb 快照持久化的时候出现的问题,上到 redis 机器查看日志定位详细问题. 可以很明显的发现应该是尝试 fork 的时候内存不够,并没有被 linux 内核放行.

变态面试

- Tony - 叫兽与你同在

RoBa:Facebook 面试 Q&A

- - 博客 - 伯乐在线
前言:本文作者 RoBa ,据其个人博客中简绍是在腾讯北京搜索部门做后台开发工作. 他最近拿到 Facebook 入职 Offer 后,不少读者对此事有些提问. 本文是 Roba 做的问题答复总结. 说实话,其实我的眼界从来很狭窄,以前想的是,如果能在天朝帝都扎下脚跟,过上老婆孩子热炕头的日子,对我来说已很满足.

Hibernate面试题

- - ITeye博客
什么是Hibernate的并发机制. Hibernate并发机制:. a、Hibernate的Session对象是非线程安全的,对于单个请求,单个会话,单个的工作单元(即单个事务,单个线程),它通常只使用一次,. 如果一个Session 实例允许共享的话,那些支持并发运行的,例如Http request,session beans将会导致出现资源争用.

java面试题

- - Java - 编程语言 - ITeye博客
 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面. 抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节. 抽象包括两个方面,一是过程抽象,二是数据抽象.  继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法. 对象的一个新类可以从现有的类中派生,这个过程称为类继承.