程序如下,判断输出多少个'_'

./a.out

int main(){
for(int i = ; i < ; ++i){
fork();
printf("_");
}
}

熟悉fork的话,这里很容易就能知道,一共产生了3个子进程,还有一个父进程,所以一共是四个进程;每次fork之后都会输出一个'_',那么在这里应当的输出是6个'_'

但是实际输出却是 8个'_'; 但是如果在printf("_")之后使用fflush(stdout),或者使用printf("\n")清空输出缓冲区来输出则结果就是6个了,这就不得不再返回来谈一谈printf的输出机制。

  • 标准I/O对待缓存的数据采用3种不同的策略,全缓冲、行缓冲、无缓冲。
  • 对于没有交互的终端,例如块设备文件,系统采用全缓冲;(比如输出到文件)
  • 对于标准输入,标准输出这样交互设备采用行缓冲;(输出到屏幕或控制台)
  • 对于需要立即响应的设备,例如标准错误采用无缓冲;

printf函数只有当缓冲区被刷新的时候才会输出数据,在此之前只是将数据存放到缓冲区。

理解了这点后,再去分析上述代码;首先只有一个进程a.out,缓冲区为空;第一次fork()后,父进程a.out产生了一个子进程“子1”,子进程复制父进程的缓冲区(此时为空),然后printf将"_"先放入缓冲区内,a.out与"子1"的缓冲区内各有一个"_";

第二次fork()后,a.out产生了一个子进程"子2",“子1”产生了一个子进程“子3”,“子2”和“子3”均从各自的父进程复制了缓冲区的“_”,此时,a.out,"子1",“子2”,“子3”各自的缓冲区内均有一个“_”,接下来的printf语句又将”_“分别放入了四个进程的缓冲区内(图中用黑色标识的_),此时,循环结束,四个进程的各自缓冲区内都分别有两个"_",总共8个,特殊之处在于,”子2“与”子3“的缓冲区内的第一个”_“是复制各自的父进程缓冲区得到的,a.out与"子1"的缓冲区内的第一个"_"是printf累积”_“的结果。如此一来,循环结束后,程序就要结束了,此时缓冲区刷新输出了8个"_".

到这里,这道题已经讲的很明白了,为了深入理解,再通过几个代码详细理解一下缓冲区的刷新机制:

int main(){
printf("hello");
//fflush(stdout);
sleep();
int m = ;
m = ;
while(m-->){
printf("*");
}
//printf("\n");
sleep();
printf("world");
}

这个程序将如何输出呢?如果去掉注释输出结果又是什么?先思考一下吧。

理想中的输出应该是先输出 "hello",等待5秒,输出7个'*',再等待3秒,输出"world";但是实际情况却是等待 8秒,一瞬间输出”hello*******world“.

如果去掉注释呢?就是我们所期盼的第一种情况了;造成这种现象的原因上面已经提到了,就是printf并非直接输出,而是先攒到缓冲区里,等待缓冲区刷新再输出;这段程序中的两条注释语句均可以刷新行缓冲策略的缓冲区(行缓冲与全缓冲在文章末尾)。

那么来看一下缓冲区刷新的时机吧

  • 1.遇到“\n”,立即刷新缓冲区。(行缓冲)
  • 2.程序调用fflush函数刷新缓冲区
  • 3.程序以exit结束,缓冲区会刷新。如果以_exit结束,缓冲区数据会被直接清空。
  • 4.缓冲区满,也会将缓冲区数据刷新出来。

这样子便不难理解了吧。可惜刚开始学习printf的时候总是习惯性在后面加一个'\n',所以这么久了这么重要的一个缓冲区机制居然才知道。

最后再用一个例子补充一下行缓冲与全缓冲的区别

int main(){
printf("hello world\r\n");
if( == fork()){
printf("son\r\n");
}else{
printf("father\r\n");
}
}

先普通执行一下输出结果为一条"hello world",一条"son",以及一条"father";

再将输出位置重定向到文件,输出结果多了一条"hello world"

同样的程序,只因为输出位置的不同,结果也就不同,这是因为第一种使用了标准输出也就是控制台和屏幕,采用行缓冲策略;第二种将标准输出重定向到文件,采用的是全缓冲策略;行缓冲策略中的'\n'对全缓冲无效;所以又回到了文章开始的那道面试题的缓冲区复制。由于是全缓冲,并不会刷新缓冲区,因此fork时子进程复制了父进程的缓冲区,里面有一条”hello world“,此时父子进程再各自往缓冲区中攒了"father"和'son';因此就呈现出最终的输出结果啦。

(这种缓冲区机制在C++中的cout也是一样的)

完。

一道值得思考的fork()面试题的更多相关文章

  1. [POI2010]KLO-Blocks——一道值得思考的题

    题目大意: 给出N个正整数a[1..N],再给出一个正整数k,现在可以进行如下操作:每次选择一个大于k的正整数a[i],将a[i]减去1,选择a[i-1]或a[i+1]中的一个加上1.经过一定次数的操 ...

  2. 一道非常棘手的 Java 面试题:i++ 是线程安全的吗

    转载自  一道非常棘手的 Java 面试题:i++ 是线程安全的吗 i++ 是线程安全的吗? 相信很多中高级的 Java 面试者都遇到过这个问题,很多对这个不是很清楚的肯定是一脸蒙逼.内心肯定还在质疑 ...

  3. 从fork面试题開始的思考

    一.文章来由 还是按照惯例来说一下文章为什么来的.晚上好基友在网上刷面试题,看到一个有趣的题目,于是開始了研究,就有了这篇文章. 二.进入正题 题目例如以下: #include <stdio.h ...

  4. 关于一道简单的Java 基础面试题的剖析: short s1=1;s1 = s1 +1会报错吗?

    package common; public class ShortTypeTest { /* * @param args */ public static void main(String[] ar ...

  5. 从一道没人能答对的面试题聊聊Java的值传递

    这是一道我们公司的面试题,从招第二个Java以来就一直存在了.但是面试了这么长的时间还没有一个人可以全部答对,让我们一度以为是这题出的不对.首先请看面试题. 以下运算的输出分别是多少: ```java ...

  6. 集合中存的是引用,分析一道容易混淆的Java面试题

    我们自定义的类是以引用的形式放入集合,如果使用不当,会引发非常隐蔽的错误.就拿我经常问到的一个面试题来说明这个知识点. 第一步,我们定义一个Car类型的类,其中只有一个int类型id属性. 第二步,创 ...

  7. javascript基础修炼(1)——一道十面埋伏的原型链面试题

    在基础面前,一切技巧都是浮云. 题目是这样的 要求写出控制台的输出. function Parent() { this.a = 1; this.b = [1, 2, this.a]; this.c = ...

  8. 一道关于js正则表达式的面试题

    这道面试题明显是要用到正则表达式来解决的,由于太久没有写正则表达式了,一时之间竟然写不出来,所以记录一下笔记,下面直接上代码: function parseUrl(str) { // 判断是否传入参数 ...

  9. OpenJDK源码研究笔记(二)-Comparable和Comparator2个接口的作用和区别(一道经典的Java笔试面试题)

    Comparable和Comparator是JDK中定义的2个比较接口,很相似,但又有所不同. 这2个接口的作用和区别也是Java中的常见经典面试题. 下面我们就来详细介绍下这2个接口的定义.作用.区 ...

随机推荐

  1. Eclipse快速入门:远程调试Java应用

    Eclipse快速入门:远程调试Java应用 2012年03月27日00:00 it168网站原创 作者:皮丽华 编辑:皮丽华 我要评论(0) 标签: Eclipse , Java , Java框架, ...

  2. 杀入红海市场 ZUK手机底气在哪?

       从越来越奢华的发布会舞台屏幕,到创意越来越烧脑的邀请函,一款新手机的发布工作变得越来越系统化.何时展示.如何亮相,都成为影响一部手机情怀,甚至销售好坏的重要因素.虽然很难以一个固定标准衡量各个手 ...

  3. Springboot整合Dubbo和Zookeeper

    Dubbo是一款由阿里巴巴开发的远程服务调用框架(RPC),其可以透明化的调用远程服务,就像调用本地服务一样简单.截至目前,Dubbo发布了基于Spring Boot构建的版本,版本号为0.2.0,这 ...

  4. 菜篮子成血战场!生鲜O2O为何厮杀如此惨烈

    衣食住行作为与大众生活直接挂钩的刚需,已经被诸多互联网企业全面渗入,并在彻底颠覆大众原有的生活形态.但其中作为最底层.最基本的"菜篮子"--生鲜市场,似乎仍然没有被互联网元素完全& ...

  5. css进阶之二:flex弹性布局

    布局模式是指一个盒子与其兄弟.祖先盒的关系决定其尺寸与位置的算法.css2.1中定义了四种布局模式,分别是块布局.行内布局.表格布局.以及定位布局.css3引入了新的布局模式Flexbox布局,灵活度 ...

  6. C++走向远洋——31(六周,项目一,1.1)

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:fenshu.cpp * 作者:常轩 * 微信公众号:World ...

  7. tfgan折腾笔记(一):核心功能简要概述

    tfgan是什么? tfgan是tensorflow团队开发出的一个专门用于训练各种GAN的轻量级库,它是基于tensorflow开发的,所以兼容于tensorflow.在tensorflow1.x版 ...

  8. swagger 以及swaggerUI使用的步骤

    1.swagger,可以这么理解swagger是接口规范.Rest Api 传递参数的除了get请求外,put post,需要传递json.或者就是直接都通过传递json到后台 这里主要介绍一下spr ...

  9. 手机浏览器自动播放视频video(设置autoplay无效)的解决方案

    1.问题的提出 某一天接了个需求,需要在手机的H5页面内加入视频,我开开心心做完,准备交付的时候,问题来了,PM想要用户一进入页面,视频就开始播放,不需要用户手动点击. 2.尝试解决 加autopla ...

  10. LVM简介及CentOS7 LVM操作实战

    LVM简介LVM是逻辑盘卷管理(LogicalVolumeManager)的简称,它是Linux环境下对磁盘分区进行管理的一种机制,LVM是建立在硬盘和 分区之上的一个逻辑层,来提高磁盘分区管理的灵活 ...