• 从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个“-”。

    下图把”-“改成”s”了
    这里写图片描写叙述

    以下開始有借鉴大牛的博客
    要讲清这个题。我们首先须要知道fork()系统调用的特性

    1. fork()系统调用是Unix下以自身进程创建子进程的系统调用。一次调用,两次返回。假设返回是0,则是子进程。假设返回值>0,则是父进程(返回值是子进程的pid)。这是众为周知的。


      这个能够有方法巧记一下,就是子进程从fork開始处開始运行。可是此时没有子进程。所以返回值是0;

    2. 另一个非常重要的东西是,在fork()的调用处。整个父进程空间会原模原样地拷贝到子进程中,包含指令,变量值。程序调用栈,环境变量。缓冲区,等等。

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

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

    对于上面的问题,我们假设改动一下上面的printf的那条语句为:

    printf("-
    ");

    或是

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

    就没有问题了(就是6个“-”了)。由于程序遇到“ ”,或是EOF,或是缓中区满。或是文件描写叙述符关闭,或是主动flush。或是程序退出,就会把数据刷出缓冲区。

    须要注意的是。标准输出是行缓冲。所以遇到“ ”的时候会刷出缓冲区。但对于磁盘这个块设备来说,“ ”并不会引起缓冲区刷出的动作,那是全缓冲,你能够使用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有“
    ”
          printf("ppid=%d, pid=%d, i=%d 
    ", 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(“-“);这个语句,我们就能够非常清楚的知道,哪个子进程复制了父进程标准输出缓中区里的的内容,而导致了多次输出了。(例如以下图所看到的,就是我阴影并双边框了那两个子进程)
    这里写图片描写叙述

    —END—


  • 相关阅读:
    C#的GroupBy方法是如何工作的
    流媒体技术探索(一)
    战争雷霆-鼠标穿透
    继承与ER图
    从零开始的文档对象模型(结束更新)
    [hackerrank] booking.com
    [lintcode][美国大公司][1.字符串处理]
    [interview] Aug. 2015
    [codility] Lesson 2 Counting Elements
    [codility] Lesson 1 Time Complexity
  • 原文地址:https://www.cnblogs.com/brucemengbm/p/7060252.html
Copyright © 2020-2023  润新知