linux系统下 fork()系统调用: 关于父子进程缓存问题的小坑
2017-06-24 11:15
387 查看
linux系统下 fork()系统调用, 关于父子进程缓存问题的小坑
1. 首先看一个简单示例程序如下
#include <stdio.h> #include <unistd.h> #include <assert.h> int main(int argc, char **argv) { printf("before fork: in father...\n"); pid_t child = fork(); assert( child >= 0 ); if ( 0 == child ) printf("in child...\n"); if ( child > 0 ) printf("after fork: in father...\n"); }
在我本机测试, 输出如下:
before fork: in father... after fork: in father... in child...
输出和我们预想一致. 一共输出3行. 目前一切正常.
请继续往下看.
2. 这个示例程序和第一个基本相同, 但是输出时, 换行符”\n”略有区别.
#include <stdio.h> #include <unistd.h> #include <assert.h> int main(int argc, char **argv) { // 注意这里没有换行, 多加了几个空格, 使得打印容易识别. printf("before fork: in father... "); pid_t child = fork(); assert( child >= 0 ); if ( 0 == child ) printf("in child...\n"); if ( child > 0 ) printf("after fork: in father...\n"); }
程序输出如下:
before fork: in father... after fork: in father... before fork: in father... in child...
这次输出就不在我们意料之中了!!!
“before fork: in father… ”
是在fork()调用之前的父进程中调用输出的.
但是竟然在子进程中也输出了 !!!
感觉好像是在fork之前父进程中的printf打印的字符, 被copy到子进程中了?
没错, 正是如此.原理如下:
我们这次在fork之前printf打印语句时, 没有带”\n”参数( 终端是行缓存的. )
所以这条语句执行后, 打印信息并没有被立刻输出到屏幕.而是缓存起来了.
终端stdout是行缓存, 输出只有在遇到’\n’字符,或者输出超过缓存长度时, 才会刷新缓存–真正写入文件( 这里的写文件就是终端stdout )
这样当调用fork() 函数时, 父进程中的输出缓存还没有刷新到文件.
fork()调用除了会复制父进程的所有已打开文件描述符, 还会复制父进程的缓存到子进程中.
所以父进程的缓存 “before fork: in father… ” 就被copy到子进程中了..
缓存复制到子进程空间后, 就像子进程自己调用了printf的效果一样.
结果程序输出就出现了上面诡异的一幕.
至于程序1为什么没有出现这样的一幕, 因为在fork之前父进程printf 带了’\n’, stdout是行缓存, 遇到’\n’时就会刷新输出到stdout的缓存.
在fork时, 父进程的缓存已刷新到文件.缓存被删除. 所以不会copy到子进程空间.
3. 这次对程序1 输出重定向到文件, 查看结果.
重定向输出到fork.log中, 代码如下:$ ./fork-out > fork.log $ cat fork.log
输出如下:
before fork: in father... after fork: in father... before fork: in father... in child...
咦等等, 程序1 不是正常吗, 为什么我仅仅重定向了一次, 结果就成这样了.
不是说’\n’ 在终端stdout是行缓存的吗, 遇到’\n’就会刷新输出缓存!
是的, 之前说的没错. 请注意:”终端stdout是行缓存的”, 但是文件重定向之后, 这时候输出stdout文件已经不是终端了, 而是重定向到了fork.log文件.
fork.log 是一个普通文件, 普通文件的缓存是基于长度的, 也就是说输出到普通文件的数据, 在缓存后, 只有达到了缓存上限, 或者手动调用 flush/sync 等系统调用刷新, 才会清空缓存并刷新到文件中.
而 “行缓存” 是只有终端stdout才有的.
同时注意终端stderr是没有缓存的, 因为输出到stderr的信息都是敏感信息. 所以系统并不缓存.
但是在stderr重定向到文件后, 也会遇到相同的缓存级别改变的情况. 读者感兴趣请自行尝试.
4. 所以正确做法是:
调用fork程序前, 手动调用flush/fflush函数手动刷新输出缓存.避免重定向后出现父子进程诡异的输出copy问题.
相关文章推荐
- 关于linux系统缓存的问题
- Linux下调用fork或system启动子进程的信号和资源释放相关问题
- 关于fork()父子进程返回值的问题
- 问题:子进程父进程哪个先执行:【转】关于 fork 和父子进程的理解
- 一道关于Linux系统下fork系统调用的面试题
- Linux下调用fork或system启动子进程的信号和资源释放相关问题
- Linux系统进程控制编程(二)——fork系统调用
- Linux下调用fork或system启动子进程的信号和资源释放相关问题
- Linux 系统调用之 fork()——进程的创建
- 一道关于Linux系统下fork系统调用的面试题
- 问题:子进程父进程哪个先执行:【转】关于 fork 和父子进程的理解 + 【转】
- 十、Linux系统编程-进程(三)父子进程共享文件、fork和vfork、exit和_exit、atexit注册退出事件
- malloc()后进行fork()系统调用,父子进程空间关系如何
- 关于fork()函数父子进程之间的问题
- 避免linux系统调用fork后产生僵死进程
- 九、Linux系统编程-进程(二)fork系统调用、复制进程映像、写时复制、孤儿进程和僵尸进程
- 一道关于Linux系统下fork系统调用的面试题
- Linux下调用fork或system启动子进程的信号和资源释放相关问题
- 关于linux系统调用fork()的一道面试题
- linux系统调用fork, vfork, clone