前言

  • 内容来源:https://www.bilibili.com/video/BV1yJ411S7r6?p=48

一、fork函数的使用异常

有一fork函数使用示例如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main()
{pid_t pid;printf("[%d]:Begin!\n", getpid());pid = fork();if(pid < 0){perror("fork()");exit(1);}if(pid == 0) {printf("[%d]:Child is working!\n", getpid());}else{printf("[%d]:Parent is working!\n", getpid());}printf("[%d]:End!\n", getpid());exit(0);
}

根据fork函数功能,我们可以在控制台上输出以下内容:

[root@localhost process_basic]# ./fork1
[20032]:Begin!
[20032]:Parent is working!
[20032]:End!
[20033]:Child is working!
[20033]:End!

即:输出一次Begin,和两次End。这个很好理解,因为fork是在父进程输出Begin之后开启子进程的,由于子进程复制了父进程的运行进度,所以自然不会输出Begin。

但是当我们不把结果输出控制台,而是把它输出到文件中:

[root@localhost process_basic]# ./fork1 > /tmp/out

打开/tmp/out之后,显示:

[21269]:Begin!
[21269]:Parent is working!
[21269]:End!
[21269]:Begin!
[21270]:Child is working!
[21270]:End!

我们会惊讶地发现,pid为21269的父进程输出一次Begin后,后面居然又输出了一次Begin,进程号依旧是父进程的id号21269。这是为什么呢?
因为终端设备的流默认是行缓冲,而磁盘的流默认是全缓冲
当程序执行到

    printf("[%d]:Begin!\n", getpid());

的时候,如果是输出到终端设备,由于printf后面的语句有换行符,流缓冲就会被刷新,原有的缓冲区里也不会有这一条语句。当父进程fork()出子进程后,自然也不可能再多出这句。
而当我们输出到文件中,即执行的是磁盘流默认的全缓冲模式,缓冲区不会被刷新,会积累到最后一起输出。所以在这种情况下,printf语句里的换行符仅仅是一个普通的换行符而已,并不会有自动刷新缓冲区的效果,开头这条Begin语句会在父进程fork()出子进程后,连带着复制进子进程的缓冲区。所以最后当程序执行到最后统一输出的时候,会输出两条Begin,而且它的所属pid都是父进程的pid。
那么该如何修改代码呢?

二、fork前进行fllush

在程序执行fork()函数前,添加上fflush,如下所示:

int main()
{printf("[%d]:Begin!\n", getpid());fflush(NULL);pid = fork();

此时,我们再把程序结果输出到/tmp/out文件中,查看结果:

[root@localhost process_basic]# ./fork1 > /tmp/out
[root@localhost process_basic]# cat /tmp/out
[21659]:Begin!
[21659]:Parent is working!
[21659]:End!
[21660]:Child is working!
[21660]:End!

就和我们输出到终端命令行的结果一样了,Begin就输出了1次。

三、fflush()在exec()中的使用场景

有一程序如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main()
{puts("Begin!");execl("/usr/bin/date", "date", "+%s", NULL);perror("execl()");exit(1);puts("End!");exit(0);
}

终端输出如下:

[root@localhost process_basic]# ./ex
Begin!
1630918521

如我们所预想的那样,函数先输出了Begin后,再输出了一串时间戳,由于新的程序替换了旧的程序,所以End字符串也不会被输出。
下面来看看输出到文件的情况:

[root@localhost process_basic]# ./ex > /tmp/out
[root@localhost process_basic]# cat /tmp/out
1630918963

可以发现程序没有输出Begin,而只输出了时间戳。
其实原因跟fork()示例中一样:
输出到终端,采用的是行缓冲模式,遇到换行符便会刷新缓冲区,输出字符;
而输出到文件,由于磁盘流默认是全缓冲模式,遇到字符串换行符后缓冲区并不会自动刷新。所以这个示例中进程还没来得及输出缓冲区中的Begin字段,老的进程映像(Process Image)就被新的进程映像所取代了。

所以在调用exec函数前,也要执行fflush()函数,如下:

int main()
{puts("Begin!");fflush(NULL);execl("/usr/bin/date", "date", "+%s", NULL);...
}

这样就解决问题了,文件中的输出结果为:

[root@localhost process_basic]# ./ex > /tmp/out
[root@localhost process_basic]# cat /tmp/out
Begin!
1630919521

APUE:fork()、exec()前用fflush()刷新缓冲区的重要性相关推荐

  1. linux printf 刷新,linux下printf中\n刷新缓冲区的疑问(已解决)

    #include #include int main(void) {          printf("hello world"); close(STDOUT_FILENO);   ...

  2. linux下printf语句执行时间,linux下printf中\n刷新缓冲区的疑问(已解决)

    #include #include int main(void) {          printf("hello world"); close(STDOUT_FILENO);   ...

  3. Unix/Linux fork/exec的前世今生

    本文是<Linux fork那些隐藏的开销>的前传<Unix/Linix fork前传>.转载注明来自公众号"Linux阅码场". 昨天(好像是上周的事了, ...

  4. Arduino编译错误解决办法:fork/exec:…\arm-none-eabi-g++.exe: The filename or extension is too long

    编译错误解决办法:fork/exec:-\arm-none-eabi-g++.exe: The filename or extension is too long 解决方案来自Edge Impulse ...

  5. docker 异常:“fork/exec /proc/self/exe: no such file”

    docker 服务不正常 [root@controller03 ~]# docker exec -it keystone bash rpc error: code = 2 desc = oci run ...

  6. gdb调试fork+exec创建的子进程的方法

    最常见的多进程的形式如下: pid = fork(); if (pid < 0) { // fork failedprintf("fork error\n");exit(1) ...

  7. 网络编程(part4)--刷新缓冲区

    鄙人学习笔记 文章目录 刷新缓冲区 举个例子1(行缓冲) 举个例子2(只有把系统默认缓冲区大小写满,才会自动刷新) 举个例子3(flush()刷新缓冲区) 刷新缓冲区 缓冲区刷新函数 flush() ...

  8. fork exec时打开文件的变化

    在分析linux系统调用fork,linux系统调用execve时,已经知道: 1.fork时,子进程会复制父进程的打开文件描述符表 2.exec时,进程的打开文件描述符表保持不变 用以下代码观察fo ...

  9. Linux下的进程控制原语【pid_t,fork,exec,wait,waitpid,getpid,kill,pasue,sleep,signal】

    目录 一.pid_t 进程号类型 二.与进程创建.执行有关的系统调用说明 fork()系统调用: exec 系统调用: wait() 和 waitpid() 系统调用: getpid()系统调用语法: ...

最新文章

  1. Python 骚操作:微信远程控制电脑
  2. 《Greenplum企业应用实战》一2.3 畅游Greenplum
  3. android新浪微博sdk登录获取用户名_多账户的统一登录方案
  4. 【高性能定时器】 时间轮
  5. SpringBoot 启动报错:Failed to configure a DataSource: ‘url‘ attribute is not specified and no emb
  6. pcl库python_成功安装 linux pcl (1.8.1版本) python-pcl
  7. 工作心得_在做算法工程师的道路上,你掌握了什么概念或技术使你感觉自我提升突飞猛进?
  8. laravel框架解决sql注入问题
  9. 电容器单位及电容器单位换算
  10. Effective Receptive Field
  11. p7510 rom android 8,三星p7510 recovery卡刷rom 刷机教程
  12. C2Prog串口烧写TMS320F28335 Flash实验
  13. python爬虫爬取豆瓣电影评分排行榜前n名的前n页影评
  14. 轻量级日志分析PLG平台Loki安装部署及使用(promtail+loki+Grafana)
  15. 【传感器大赏】3轴磁场传感器
  16. securefx 堡垒机_安恒堡垒机参数
  17. 结对作业项目报告——四则运算UI设计(UI第一组 PB16120211 章豪 PB16151063 吴宏宇)...
  18. CoffeeScript语法
  19. 1905协议详解(四)数据帧分析总览
  20. 当面试官问【你还有什么想问的吗】时,应该问什么

热门文章

  1. Ubutntu下使用realsense d435i(三):使用yolo v5测量目标物中心点三维坐标
  2. ubuntu安装有道词典命令行查询
  3. 三角形中的正方形,三个问题
  4. 小米路由器设置DMZ主机 并在外网访问
  5. 数据结构——左倾红黑树
  6. 手机端的多图片剪辑上传支持手势支持预览
  7. 三年黑盒测试工程师对嵌入式软件测试的理解
  8. linux驱动学习2(kpd驱动初步分析)
  9. MTK keypad调试,扩张键盘IC AW9523
  10. 互联网大厂的黑话困局