fork()与输出缓冲区的一个坑

本文探讨了在使用fork()创建子进程时,如何因printf的输出缓冲区导致意外结果的问题。当父子进程同时写入标准输出时,数据会被暂存于各自缓冲区。由于子进程继承了父进程的缓冲区,导致最终输出的数量增加。为避免这种情况,可以通过添加换行符或使用fflush(stdout)主动清空缓冲区来确保预期输出。
摘要由CSDN通过智能技术生成

题目:请问下面的程序一共输出多少个“A”?

		#include <stdio.h>
        #include <sys/types.h>
        #include <unistd.h>
        int main(void)
   		{
         	
            int i;
            for(i=0; i<2; i++)
            {
              	fork();
              	printf("A");
           	}
          	return 0;
      	}

经过分析我们知道是6个,自己画了一张图
如下:
在这里插入图片描述
下面的代码呢?

   		#include <stdio.h>
        #include <sys/types.h>
        #include <unistd.h>
        int main(void)
   		{
         	
            int i;
            for(i=0; i<2; i++)
            {
              	fork();
              	printf("A");
           	}
          	return 0;
      	}
      

最后会输出6个A,结果答案不是6,而是8。

究其原因,主要有两个因素决定最后结果,一个是fork调用的作用,一个是printf标准I/O调用的输出缓冲区在作怪。

一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程。fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。

注意,子进程持有的是上述地址空间的“副本”,这意味着父子进程间不共享这些存储空间。

Linux中对文件系统的读写可以通过系统调用read和write来完成用户写数据时内核首先会把用户数据拷贝到内核高速缓冲中,然后在采取延迟写策略写到文件中去。这两个系统调用是通过文件描述符和输入输出关联,但是这就涉及到需要用户来管理缓冲区,因为不同的缓存区的长度会影响读写的效率。为了克服这个缺陷,后来开发出了标准I/O库,不再通过文件描述符和输入输出关联,而是通过FILE指针和流关联,FILE结构中包括了文件描述符,缓冲区的大小,当调用fopen的时候系统会动态分配一块内存作为缓存区。而printf作为标准I/O中的函数,也有自己的缓冲区。

  • 标准I/O对待缓存的数据采用3种不同的策略,全缓冲、行缓冲、无缓冲。
  • 对于没有交互的终端,例如块设备文件,系统采用全缓冲;
  • 对于标准输入,标准输出这样交互设备采用行缓冲;
  • 对于需要立即响应的设备,例如标准错误采用无缓冲;

所以printf调用输出到标准输出将采用行缓冲策略。当i=0时,fork调用后,父子进程同时输出‘A’,此时‘A’并不会立即输出到终端显示屏,而是会暂存在各自的缓冲区内,而当i=1时,再次fork时,由于子进程是父进程的副本,这样这两个进程的孩子进程将得到他们父进程的缓冲区,这就是最后答案会由6到8的原因,

可用下图表示为:
在这里插入图片描述

所以,为了防止这种不是我们希望的结果出现,就需要采取相应的措施,对于行缓冲,程序在遇到“\n”,或是EOF,或是缓中区满,或是文件描述符关闭,或是主动flush,或是程序退出,都会把数据刷出缓冲区。
这样如果我们这样输出:

printf(“A\n”);

或者主动洗刷缓冲区

fflush(stdout);

都会把数据刷出缓冲区。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值