APUE:fork()、exec()前用fflush()刷新缓冲区的重要性
前言
- 内容来源: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()刷新缓冲区的重要性相关推荐
- linux printf 刷新,linux下printf中\n刷新缓冲区的疑问(已解决)
#include #include int main(void) { printf("hello world"); close(STDOUT_FILENO); ...
- linux下printf语句执行时间,linux下printf中\n刷新缓冲区的疑问(已解决)
#include #include int main(void) { printf("hello world"); close(STDOUT_FILENO); ...
- Unix/Linux fork/exec的前世今生
本文是<Linux fork那些隐藏的开销>的前传<Unix/Linix fork前传>.转载注明来自公众号"Linux阅码场". 昨天(好像是上周的事了, ...
- 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 ...
- docker 异常:“fork/exec /proc/self/exe: no such file”
docker 服务不正常 [root@controller03 ~]# docker exec -it keystone bash rpc error: code = 2 desc = oci run ...
- gdb调试fork+exec创建的子进程的方法
最常见的多进程的形式如下: pid = fork(); if (pid < 0) { // fork failedprintf("fork error\n");exit(1) ...
- 网络编程(part4)--刷新缓冲区
鄙人学习笔记 文章目录 刷新缓冲区 举个例子1(行缓冲) 举个例子2(只有把系统默认缓冲区大小写满,才会自动刷新) 举个例子3(flush()刷新缓冲区) 刷新缓冲区 缓冲区刷新函数 flush() ...
- fork exec时打开文件的变化
在分析linux系统调用fork,linux系统调用execve时,已经知道: 1.fork时,子进程会复制父进程的打开文件描述符表 2.exec时,进程的打开文件描述符表保持不变 用以下代码观察fo ...
- Linux下的进程控制原语【pid_t,fork,exec,wait,waitpid,getpid,kill,pasue,sleep,signal】
目录 一.pid_t 进程号类型 二.与进程创建.执行有关的系统调用说明 fork()系统调用: exec 系统调用: wait() 和 waitpid() 系统调用: getpid()系统调用语法: ...
最新文章
- Python 骚操作:微信远程控制电脑
- 《Greenplum企业应用实战》一2.3 畅游Greenplum
- android新浪微博sdk登录获取用户名_多账户的统一登录方案
- 【高性能定时器】 时间轮
- SpringBoot 启动报错:Failed to configure a DataSource: ‘url‘ attribute is not specified and no emb
- pcl库python_成功安装 linux pcl (1.8.1版本) python-pcl
- 工作心得_在做算法工程师的道路上,你掌握了什么概念或技术使你感觉自我提升突飞猛进?
- laravel框架解决sql注入问题
- 电容器单位及电容器单位换算
- Effective Receptive Field
- p7510 rom android 8,三星p7510 recovery卡刷rom 刷机教程
- C2Prog串口烧写TMS320F28335 Flash实验
- python爬虫爬取豆瓣电影评分排行榜前n名的前n页影评
- 轻量级日志分析PLG平台Loki安装部署及使用(promtail+loki+Grafana)
- 【传感器大赏】3轴磁场传感器
- securefx 堡垒机_安恒堡垒机参数
- 结对作业项目报告——四则运算UI设计(UI第一组 PB16120211 章豪 PB16151063 吴宏宇)...
- CoffeeScript语法
- 1905协议详解(四)数据帧分析总览
- 当面试官问【你还有什么想问的吗】时,应该问什么