标准输出缓存在多进程代码中引起的一个问题

这里先看两个例子:

1) 源码如下:

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

int main(int argc, char *argv[])
{
        pid_t pid;

        fprintf (stderr, "this is stderr string!\n");
        printf ("this is stdout string!\n");

        if ((pid = fork()) == 0)
        {

        }
        else if (pid > 0)
        {

        }
        else
        {
                fprintf (stderr, "fork error! errno: %d errstr: %s\n", errno, strerror(errno));
        }

        exit(0);
}

运行:
在这里插入图片描述​​

问:这里 printf ("this is stdout string!\n");为什么会在文件中打印两句出来?而在不重定向的情况下只打印一次;

2) 这是一个进程间的 pipe 通信,代码如下:

// main_process.c
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
        int pipe_fd[2];
        pid_t pid;
        char pipe_buf[1024];
        int ret;

        if (pipe(pipe_fd) < 0)
                fprintf(stderr, "pipe failed!\n");

        if ((pid = fork()) == 0) /* child */
        {
                close(pipe_fd[0]);
                dup2(pipe_fd[1], STDOUT_FILENO);
                close(pipe_fd[1]);
                if (execl("./child_process", "./child_process", NULL) == -1)
                        fprintf (stderr, "execl faild!\n");

                exit(0);
        }
        else if (pid > 0) /* parent */
        {
                close(pipe_fd[1]);
                while ((ret = read(pipe_fd[0], pipe_buf, sizeof(pipe_buf) - 1)) > 0)
                {
                        pipe_buf[ret] = '\0';
                        printf ("%s\n", pipe_buf);
                        fflush(stdout);
                }
                close(pipe_fd[0]);
        }
        else
                fprintf (stderr, "fork failed!\n");

        exit(0);
}



// child_process.c
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
        int i;

        for (i=0; i<100; i++)
        {
                printf ("counter: %d\n", i);
                sleep(1);
        }

        exit(0);
}

我们的本意是将 child 进程的输出重定向,然后从 main 进程中接收并打印出来,根据程序也就是 1s 打印一次计数,但这个程序在运行中会出现什么问题呢?main 进程的 read 会一直阻塞,直到 child 进程将 pipe_fd[1] 写满以后才会有返回打印出。

其实,这两个问题都源于一个原因,那就是缓冲的原因。

在输入输出的缓冲中,我们知道有三种:无缓冲行缓冲块缓冲(也叫全缓冲)

对于标准输出/标准错误输出,默认的缓冲方式是:标准错误输出是无缓冲输出,也就是只要给stderr写数据,马上就会送出标准输出分两种情况,如果输出定向的是到设备终端(tty),那么是行缓冲,如果是其他设备/文件,则是块缓冲

这就可以解释上面两种情况了:

  1. 当重定向到文件输出时,printf 的内容会在缓冲中,fork 时会将当前的缓冲区 clone 一个,而且内容不变,但进程结束的时候,系统会自动 flush 缓冲区,所以子进程和服进程均将缓冲区的内容输出,所以出现了两次同样的 printf 的内容;
  2. 主进程将子进程的 stdout 重定向到 pipe 输出,所以 child 就会块缓冲,所以并不是每秒去 flush 一下 buffer,而是当 buffer 满或者 child 进程关闭时 flush,故 main 进程的 read 执行情况就不是我们的本意了。

弄清楚了原因,解决这个问题也有两种方法:

  1. printf 后,使用 fflush(stdout) 手动 flush 缓冲区;
  1. 使用 setbufsetvbuf 设置 stdout 的缓冲方式;

小结:
由于 stdout 在定向的位置不一样会有不一样的缓冲方式,所以在写被调用的子进程的时候一定要注意这个问题,最好在使用 stdout 的地方使用 fflush 进行手动 flush。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值