文件缓冲区在fork后复制

场景:父进程trace进程A,当A进程fork子进程B时,让父进程也fork子进程去trace子进程B,用于trace的进程将被trace的进程发生的系统调用号通过fprintf存入各自文件中

问题:printf输出正确,而fprintf到文件的内容会重复(其实就知道了是缓冲问题)

解决:在调用fork()前用fflush(fp);清空文件缓冲区

 

原因:在fork()的调用处,整个父进程空间会被复制到子进程中,包括指令,变量值,程序调用栈,环境变量,缓冲区,等等。

延伸到stdio缓冲区:stdio缓冲区在进程的用户空间内存中,通过fork()创建的子进程会复制这些缓冲区,当标准输出定向到终端时,因为缺省为行缓冲,所以会显示printf输出的包含换行符的字符串。

不过,当标准输出重定向到文件时,由于缺省块缓冲,所以调用fork()时,printf()输出的字符串仍然在父进程的缓冲区中,并随子进程的创建而产生一份副本。

父,子进程调用exit()时会刷新各自stdio缓冲区,从而导致重复输出的结果。

 

知识:

标准I/O库提供了三种类型的缓冲:

(1) 全缓冲:填满标准I/O缓冲区才实际进行I/O操作。

(2)行缓冲:在输入和输出中遇到换行符时,标准I/O库执行I/O操作,当流涉及终端时,通常使用行缓冲。

(3)不带缓冲:标准出错流stderr通常是不带缓冲的。

上面说的缓冲指的是应用层的缓冲,在进行实际的I/O操作时,相关的系统调用(read和write)其实在内核也有缓冲区的。

当输出到终端时,由于是行缓冲,所以遇到换行符'\n'后缓冲区被冲洗。

当将程序输出重定向到文件时,标准输出是全缓冲,fork之前printf的数据仍在缓冲区中,在fork时该缓冲区也被复制到子进程中,因此输出两次。

 

实验:

#include<stdio.h>
#include<stdlib.h>
#include <unistd.h>

int main()
{
    FILE *fp = fopen("./1.txt", "wt+");
    if(fp == NULL)
    {
        return -1;
    }
    printf("xxx\n");
    fprintf(fp, "%d\n", 111);
    //fflush(fp);//调用fork()之前使用函数fflush()来刷新stdio缓冲区
    if(!fork())
    {
        exit(0);
        //_exit(0);//子进程调用_exit(0)而非exit()以不再刷新stdio缓冲区
    }

    return 0;
}

1、输出到终端(行缓冲)

2、重定向到文件xxx.txt(块缓冲)

3、fprintf到文件1.txt(fprintf函数在默认情况下只有当缓冲区达到一定条件或是程序自然停止时才会输出数据到文件)

可以看到2和3都重复输出,用上面注释的方法即可解决该问题

 

概念参考:

https://blog.csdn.net/dextrad_ihacker/article/details/52033223

http://www.cnblogs.com/tonychen-tobeTopCoder/p/5335452.html

posted @ 2018-05-24 20:23  前进的code  阅读(572)  评论(0编辑  收藏  举报