zoukankan      html  css  js  c++  java
  • 关于linux的fork的一点学习总结

    最近操作系统的实验要用到fork,于是去搜索了一下资料,很幸运地在博客中找到一篇深度好文:

    http://blog.csdn.net/jason314/article/details/5640969

    讲的非常详细,分析得很好,读完能够对fork有个较好的理解。这里就上面的博客中提到的个别代码进行尝试和分析。

    首先是进阶知识的第一份代码,我在自己的机子上尝试了一下,试着看看运行结果并进行分析。

    #include <stdio.h>
    #include <unistd.h>
    
    int main(void){
    
        int i = 0;
        printf("i  son/pa  ppid  pid  fpid
    ");
        
        for(i = 0; i < 2; ++i){
            pid_t fpid = fork();
            if(fpid==0){
                printf("%d child  %4d %4d %4d
    ", i,getppid(),getpid(),fpid);
            }else{
                printf("%d parent %4d %4d %4d
    ", i,getppid(),getpid(),fpid);
            }
        }
        return 0;
    }

    代码照搬上面的博客代码,接下来我们看测试的结果。

    运行了几次,输出的顺序可能和作者的不一样,但因为是并发,所以自然而然每次执行的顺序都会不同,根据调度可能会出现不同情况也是很正常的。但是后两次,其实与上面作者的结果很相似,就是父进程都退出了而子进程还在运行(但我们可以看到有的时候父进程没有退出那么快的话,还是存在fork出来的子进程没有成为孤儿而被收养的情况)。但是唯一不同的是在我的机子上,孤儿进程是由pid=1610的进程收养的,与很多资料上讲的被pid为1的init进程收养不同。

    于是ps看了一下这是啥玩意儿,

    无奈没有看懂哈哈哈。百度逛了一下,很快在http://blog.csdn.net/lianghe_work/article/details/47659267发现原来也有可能是64位机的问题,因为我的机子是64位的。于是用orphan.c来测试孤儿进程到底被哪个进程所收养,得出的结果果然是1610。代码图方便还是copy的上面查阅到关于孤儿进程的那篇博客的测试代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    
    int main(){
        
        pid_t fpid;
        fpid = fork();
        if(fpid < 0){
            perror("fork error");
            exit(1);
        } else if(fpid == 0){
            sleep(2);    //确保父进程先结束
            printf("son process: [son id] = %d, [father id] = %d
    ", getpid(),getppid());
            exit(0);
        } else if(fpid > 0){
            printf("father process, i am exited
    ");
            exit(0);
        }
    
        return 0;
    }

    下图的结果是因为子进程sleep(2),让出了cpu,所以自然被shell抢到了cpu,于是就得到了先输出“father process, i am exited”然后是命令行然后才输出"son process: [son id] = 3884, [father id] = 1610"这样的结果。所以收养孤儿进程的明显为pid=1610的进程,和想象中的没错。

    但是过了两天,又重新遇到孤儿进程的问题,重新测试了一下,发现收养孤儿进程的pid变成了1475。ps 1475来看了一下,还是/sbin/upstart --user 这个进程。

    所以我怀疑每次都是由upstart这个进程来收养孤儿进程的。查了一下upstart这个东东,发现了upstart的功能:用于linux开机自动启动某些后台服务,同时还承担监控这些服务运行状态的功能。后来发现了这篇博客,https://www.ibm.com/developerworks/cn/linux/1407_liuming_init2/,里面讲述的很清楚,在ubuntu为什么是由upstart收养孤儿进程而不是由pid=1的init进程收养,原因就是在ubuntu现在采用了自己开发的upstart来初始化系统,放弃了之前一直沿用的init包。

    好了,回到正题fork,看到最后的进阶问题,这让我想起了前两天看到的一道很有趣的习题,是和fork以及c有关的,据说是腾讯的面试题,看完这篇博客对这道题的印象就更加深刻了。这道题的题目是这样的:

    #include <stdio.h>
    
    int main(){
        printf("Hello ");
        fork();
        printf("Hello ");
        return 0;
    }

    请问以上代码,一共会输出多少个“Hello”。

    在没有特别了解fork和c的printf缓冲机制的情况下,我下意识地选了4次,虽然结果是对的,但是最后看了各路大神的解释才算是真正明白为什么是4次,经过今天看到的这篇讲fork的深度好文以后,我印象就更加深刻了。在原文中其实也有提到,c的printf缓冲机制是这样的:printf某些内容时,操作系统仅仅是把该内容放到了缓冲队列stdout里,并没有实际的写到屏幕上。但是,只要看到换行符号' ',则会立即刷新缓冲队列stdout。因为,输出4次“Hello”的原因是:第一个printf的“Hello”由于没有换行符‘ ’,所以并不马上输出,而是放到了缓冲队列中,fork()执行以后,子进程对父进程的数据和代码都进行了复制,所以缓冲队列中的“Hello”也一并被复制过去了,因为父进程和子进程在执行最后一条printf的时候,清空缓冲队列,各输出了“Hello Hello ”,故一共输出了4次“Hello”。当然了光说无凭,我们还是运行一下代码看看结果如何吧:

    果然输出了4次“Hello”。好了那么我们考虑一下,把第一个printf("Hello ")改成printf("Hello "),结果会怎么样呢?

    按照上面所说的printf缓冲机制,遇到换行符 马上输出清空缓存,那么结果就应该是第一次输出Hello和换行,然后fork,然后再输出两个Hello,一共输出三个Hello对不对?我们稍微修改一下代码,进一步验证c的printf缓冲机制是否真的如此:

    果然和我们想象中的结果一致。

    到此我们对fork函数以及c的printf的缓冲机制都有了较好的了解和比较深刻的印象。

  • 相关阅读:
    Python 学习笔记(九)Python元组和字典(二)
    Python 学习笔记(九)Python元组和字典(一)
    Java适配器模式
    Java原型模式
    Java建造者模式
    java工厂模式
    封装图片处理方法
    TP中的图片水印
    THINKphp中复杂的查询
    THINKphp中常见的Request请求类
  • 原文地址:https://www.cnblogs.com/Cccarl/p/6601765.html
Copyright © 2011-2022 走看看