最近操作系统的实验要用到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\n"); for(i = 0; i < 2; ++i){
pid_t fpid = fork();
if(fpid==0){
printf("%d child %4d %4d %4d\n", i,getppid(),getpid(),fpid);
}else{
printf("%d parent %4d %4d %4d\n", 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\n", getpid(),getppid());
exit(0);
} else if(fpid > 0){
printf("father process, i am exited\n");
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里,并没有实际的写到屏幕上。但是,只要看到换行符号'\n',则会立即刷新缓冲队列stdout。因为,输出4次“Hello”的原因是:第一个printf的“Hello”由于没有换行符‘\n’,所以并不马上输出,而是放到了缓冲队列中,fork()执行以后,子进程对父进程的数据和代码都进行了复制,所以缓冲队列中的“Hello”也一并被复制过去了,因为父进程和子进程在执行最后一条printf的时候,清空缓冲队列,各输出了“Hello Hello ”,故一共输出了4次“Hello”。当然了光说无凭,我们还是运行一下代码看看结果如何吧:

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

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

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

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

关于linux的fork的一点学习总结的更多相关文章

  1. c/c++ linux 进程 fork wait函数

    linux 进程 fork wait函数 fork:创建子进程 wait:父进程等待子进程结束,并销毁子进程,如果父进程不调用wait函数,子进程就会一直留在linux内核中,变成了僵尸进程. for ...

  2. linux中fork, source和exec的区别

    转:linux中fork, source和exec的区别 shell的命令可以分为内部命令和外部命令. 内部命令是由特殊的文件格式.def实现的,如cd,ls等.而外部命令是通过系统调用或独立程序实现 ...

  3. LINUX内核分析第四周学习总结——扒开系统调用的“三层皮”

    LINUX内核分析第四周学习总结--扒开系统调用的"三层皮" 标签(空格分隔): 20135321余佳源 余佳源 原创作品转载请注明出处 <Linux内核分析>MOOC ...

  4. Linux的fork()写时复制原则(转)

    写时复制技术最初产生于Unix系统,用于实现一种傻瓜式的进程创建:当发出fork(  )系统调用时,内核原样复制父进程的整个地址空间并把复制的那一份分配给子进程.这种行为是非常耗时的,因为它需要: · ...

  5. Linux内核分析第四周学习总结——系统调用的工作机制

    Linux内核分析第四周学习总结--系统调用的工作机制 内核态 执行级别高,可以执行特权指令,访问任意物理地址,在intel X86 CPU的权限分级为0级. 用户态 执行级别低,只能访问0x0000 ...

  6. 【转】linux 中fork()函数详解

    在看多线程的时候看到了这个函数,于是学习了下,下面文章写的通俗易懂,于是就开心的看完了,最后还是很愉快的算出了他最后一个问题. linux 中fork()函数详解 一.fork入门知识 一个进程,包括 ...

  7. 用 set follow-fork-mode child即可。这是一个 gdb 命令,其目的是告诉 gdb 在目标应用调用fork之后接着调试子进程而不是父进程,因为在 Linux 中fork系统调用成功会返回两次,一次在父进程,一次在子进程

    GDB的那些奇淫技巧 evilpan 收录于 Security  2020-09-13  约 5433 字   预计阅读 11 分钟  709 次阅读  gdb也用了好几年了,虽然称不上骨灰级玩家,但 ...

  8. 完全用Deepin Linux娱乐、工作、学习(1)

    截至今天我已经用全Deepin Desktop Linux环境娱乐.工作.学习了100多天.当你看到这个桌面的时候,会不会觉得它是MacOS?错了,它是Deepin Desktop Linux,而且它 ...

  9. 【转】Linux下Fork与Exec使用

    Linux下Fork与Exec使用 转自 Linux下Fork与Exec使用 一.引言 对于没有接触过Unix/Linux操作系统的人来说,fork是最难理解的概念之一:它执行一次却返回两个值.for ...

随机推荐

  1. DP之背包经典三例

    0/1背包 HDU2602 01背包(ZeroOnePack): 有N件物品和一个容量为V的背包,每种物品均只有一件.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使价值总和最大 ...

  2. python+echarts+flask实现对全国疫情数据的爬取并可视化展示

    用Python进行数据爬取并存储到数据库,3.15学习总结(Python爬取网站数据并存入数据库) - 天岁 - 博客园 (cnblogs.com) 通过echarts+flask实现数据的可视化展示 ...

  3. 数据结构逆向分析-Vector

    数据结构逆向分析-Vector 这个应该是家喻户晓了的东西把,如果说C/C++程序员Vector都不用的话,可能就是一个不太好的程序员. Vector就是一个STL封装的动态数组,数组大家都知道是通过 ...

  4. 免费iApp后台-云接口

    免费稳定,UI易懂简洁,功能强大 应用名称:云接口 应用版本:1.5.9 应用大小:3.55 MB 适用平台:Android(安卓) 应用用处:详情请下载软件 软件安全无毒 更新内容: 1.支付宝当面 ...

  5. PHP中的“重载”是个啥?

    很多面试官在面试的时候都会问一些面向对象的问题,面向对象的三大特性中,多态最主要的实现方式就是方法的重载和重写.但是在PHP中,只有重写,并没有完全的重载能力的实现. 重写,子类重写父类方法. // ...

  6. ssh 执行 shell脚本执行jps时:-bash: jps: command not found

    转至: https://www.codeleading.com/article/67592908468/ 我构建了hadoop集群.我们一定会写一个shell脚本去每一个节点上去jps,查看每个节点的 ...

  7. Jmeter系列(27)- 常用逻辑控制器(6) | 如果(if)控制器If Controller

    如果(if)控制器(If Controller) 在实际工作中,当使用JMeter做性能脚本或者接口脚本时,当遇到需要对不同的条件做不同的操作时,我们可以使用JMeter中if控制器来实现 if控制器 ...

  8. Linux系列(27) - 三剑客grep、awk、sed

    Linux下一切皆文件,对Linux的操作就是对文件的处理 Linux中最重要的三个命令在业界被称为"三剑客",它们是awk,sed,grep 正则表达式就好比一个模版,这个模板就 ...

  9. Pycharm新建模板默认添加作者时间等信息(逼格更高,好像很历害的样子)

    在pycharm使用过程中,关于代码编写者的一些个人信息快捷填写,使用模板的方式比较方便. 方法如下: 1.打开pycharm,选择File-Settings 2.选择Editor--Color&am ...

  10. 深入浅出WPF-06.Binding(绑定)02

    Binding对数据的校验和转换 1)数据校验,通过Binding的ValidationRules属性(Collection类型)进行校验.可以设置多个数据校验条件. <Slider Margi ...