当两个管道属于同一进程时,如何冲洗管道以清理C中的缓冲区? [英] How to flush pipes to clean the buffer in C, when both pipes are part of same process?

查看:88
本文介绍了当两个管道属于同一进程时,如何冲洗管道以清理C中的缓冲区?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在X函数中,我正在使用管道缓冲(printfs的对,这是从X函数内部调用的Y函数打印的)stdout流,如果有一个Fd,然后在缓冲完成之后,请关闭一个管道和其他Fd,然后在其上使用printf.

我想完全确定缓冲区为空,下次再次调用此X函数来执行任务时. 我尝试了一些在网上找到的东西:

  1. fflush
  2. ioctl_flushlbf:看起来gcc不支持它们. g ++支持吗?
  3. fseek(stdin,EOF,SEEK_END);
  4. fpurge(ofp);

我多次调用X()函数.如果下一组输出大于上一组,则我编写的当前代码可以正常工作.

如果下一组输出小于当前输出组.然后下一组有一些额外的垃圾值,这给了我一个指示,即缓冲区可能尚未完全刷新.

由于某些原因,我已经用C语言编写了完整的代码,但是使用的是g ++编译器.

我的代码如下:

void X(int pairs,char* expOut)
{
    char buf[256];
    int fds[2];
    char output[300];
    char input[50];

    /* opening pipes */
    pipe(fds);

    /* saving the the given stdout stream */
    int bak = dup(STDOUT_FILENO);

    /* associating Fds[1] pipe with stdout */
    int res=dup2(fds[1],STDOUT_FILENO);

    /* associating Fds[0] pipe with stdin */
    dup2(fds[0],STDIN_FILENO);
    assert(res!=-1);

    /* Call To function Y: function combParenthesis is a recursive function,
     which prints out some strings couple of time */
    combParenthesis(pairs) ;

    fflush(stdout);

    /* closing stdout FD stream */
    close(fds[1]);
    fflush(stdout);

    /* restoring the old stdout stream */
    dup2(bak, 1);
    close(bak);

    /* opening, stdin stream for reading */
    FILE *ofp = fdopen(fds[0], "r");

    char strs[30][30];
    for (int i=0;i<30;i++) {
        memset(strs[i], 0, 30);

    }

    int i=0;
    if (ofp)
    {
        int sz;
        if((pairs*2)+1 <= 1)
        {
            sz=5;
        }
        else
        {sz = (pairs*2)+1 ;}

        /* read the stream  line by line */
        while (fgets(buf,sz ,ofp)) {
            printf("\n next string %s", buf);

            i++;
        }

        if (ferror(ofp)) {
            printf("something went wrong in the input to printf");

        }

    }

    /* different ways to flush it out  */
    char c;
    while( (c = fgetc( ofp )) != EOF && c != '\n' );
    fseek(stdin,EOF,SEEK_END);
    fpurge(ofp);
    fclose(ofp);
    fflush(stdin);
    // _flushlbf();

    /* close the  fd associated with stdin */
    close(fds[0]);    
}

解决方案

fflush定位末尾的精度.

我设法使用自己的combParenthesis来运行您的代码(第一遍:中等字符串,第二遍:较大的字符串,第三个较小的字符串).我从未在输出中发现垃圾...只要用stderr

上的输出替换了您的printf("\n next string %s", buf);

       fprintf(stderr, "\n next string %s", buf);

我也通过在stdin和ofp之后用单个fflush(stdout)刷新stdout来获得正确的输出.恕我直言,问题是:

  • 关闭管道,以便不再将stdout复制到ofp或stdin
  • 您使用printf("\n next string %s", buf) 在stdout上进行写操作,并且stdout可能会被缓冲
  • 您刷新已经干净的流
  • 当您在下次通话中重复stdout时,您可能会阅读stdout中最后一次传递的内容

这是我发现的主要改进:

  • 如果使用管道 duping stdout读取的combParenthesis输出,切勿在任何地方写入stdout(或确保将其刷新)
  • 您做了不必要的dup2(fds[0],STDIN_FILENO);,因为您直接在管道的另一端阅读(更好)
  • 您永远不会验证sz = (pairs*2)+1小于sizeof(buf)

实际上,我已经意识到,即使在将复制到fds [1]之前刷新stdout,即使在应用程序中其他位置混用printf,您的代码也可以工作. /p>

因此,这是X()函数的固定版本,带有明确标识的修改(但恕我直言,您也应该考虑我的其他建议):

void X(int pairs,char* expOut)
{
    char buf[256];
    int fds[2];
    char output[300];
    char input[50];

    /* BEGIN EDIT */
    /* first flush stdout */
    fflush(stdout);
    /* END EDIT */

    /* opening pipes */
    pipe(fds);

    /* saving the the given stdout stream */
    int bak = dup(STDOUT_FILENO);

    /* associating Fds[1] pipe with stdout */
    int res=dup2(fds[1],STDOUT_FILENO);

    /* associating Fds[0] pipe with stdin */
    dup2(fds[0],STDIN_FILENO);
    assert(res!=-1);

    /* Call To function Y: function combParenthesis is a recursive function,
     which prints out some strings couple of time */
    combParenthesis(pairs) ;


    /* closing stdout FD stream */
    close(fds[1]);
    fflush(stdout);

    /* restoring the old stdout stream */
    dup2(bak, 1);
    close(bak);

    /* opening, stdin stream for reading */
    FILE *ofp = fdopen(fds[0], "r");

    char strs[30][30];
    for (int i=0;i<30;i++) {
        memset(strs[i], 0, 30);

    }

    int i=0;
    if (ofp)
    {
        int sz;
        if((pairs*2)+1 <= 1)
        {
            sz=5;
        }
        else
        {sz = (pairs*2)+1 ;}

        /* read the stream  line by line */
    // EDIT : changed sz with sizeof(buf)-1 - cause: no correct pairs value
        while (fgets(buf, sizeof(buf) - 1,ofp)) {
            printf("\n next string %s", buf);

            i++;
        }

        if (ferror(ofp)) {
            printf("something went wrong in the input to printf");

        }

    }

    /* different ways to flush it out  */
    /* BEGIN EDIT : ALL FLUSHING COMMENTED OUT 
    char c;
    while( (c = fgetc( ofp )) != EOF && c != '\n' );
    fseek(stdin,EOF,SEEK_END);
    fpurge(ofp);
    fclose(ofp);
    fflush(stdin);
    // _flushlbf();
    END EDIT */

    /* close the  fd associated with stdin */
    close(fds[0]);    
}

我从不对读取的内容进行垃圾处理,即使将输出重定向到文件也可以正常工作,但是由于我无法解释的原因,消息的顺序不是我期望的.希望这对您来说无关紧要

In a X function, I am using pipes, to buffer(couple of printfs, which are printed in Y function called inside from X function) the stdout stream if one Fd and then after buffer is complete,close one pipe and other Fd and then use printf on it.

I want to be completely sure that buffer is empty, when next time this X function is called again to do the task. I tried couple of things which I found online:

  1. fflush
  2. ioctl, _flushlbf: looks like they aren't supported by gcc. Does g++ support it?
  3. fseek(stdin,EOF,SEEK_END);
  4. fpurge(ofp);

I call X() function, couple of times. The present code, I have written, works fine if next set of output is greater than previous set.

If next set of output is less the present set of output. Then next set have some extra garbage values, which gives me an indication , that buffer may have not flushed completely.

Because of certain reason, I have written entire code in C but using g++ compiler.

My code as follows:

void X(int pairs,char* expOut)
{
    char buf[256];
    int fds[2];
    char output[300];
    char input[50];

    /* opening pipes */
    pipe(fds);

    /* saving the the given stdout stream */
    int bak = dup(STDOUT_FILENO);

    /* associating Fds[1] pipe with stdout */
    int res=dup2(fds[1],STDOUT_FILENO);

    /* associating Fds[0] pipe with stdin */
    dup2(fds[0],STDIN_FILENO);
    assert(res!=-1);

    /* Call To function Y: function combParenthesis is a recursive function,
     which prints out some strings couple of time */
    combParenthesis(pairs) ;

    fflush(stdout);

    /* closing stdout FD stream */
    close(fds[1]);
    fflush(stdout);

    /* restoring the old stdout stream */
    dup2(bak, 1);
    close(bak);

    /* opening, stdin stream for reading */
    FILE *ofp = fdopen(fds[0], "r");

    char strs[30][30];
    for (int i=0;i<30;i++) {
        memset(strs[i], 0, 30);

    }

    int i=0;
    if (ofp)
    {
        int sz;
        if((pairs*2)+1 <= 1)
        {
            sz=5;
        }
        else
        {sz = (pairs*2)+1 ;}

        /* read the stream  line by line */
        while (fgets(buf,sz ,ofp)) {
            printf("\n next string %s", buf);

            i++;
        }

        if (ferror(ofp)) {
            printf("something went wrong in the input to printf");

        }

    }

    /* different ways to flush it out  */
    char c;
    while( (c = fgetc( ofp )) != EOF && c != '\n' );
    fseek(stdin,EOF,SEEK_END);
    fpurge(ofp);
    fclose(ofp);
    fflush(stdin);
    // _flushlbf();

    /* close the  fd associated with stdin */
    close(fds[0]);    
}

解决方案

EDIT: a precision at the end on fflush positioning.

I managed to run your code simply using a combParenthesis of my own (first pass : medium strings, second : larger strings, third smaller). I never found garbage in output ... as soon as I have replaced your printf("\n next string %s", buf); by an output on stderr

       fprintf(stderr, "\n next string %s", buf);

I also got correct output by flushing stdout after stdin and ofp by a single fflush(stdout). IMHO the problem was :

  • you close the pipe so stdout is no longer copied to ofp or stdin
  • you write on stdout with printf("\n next string %s", buf) and stdout may be buffered
  • you flush streams that are allready clean
  • when you dup stdout on your next call, you may read what remained from last pass in stdout

Here are the main improvements that I found :

  • if your read output of combParenthesis with a pipe duping stdout, never write to stdout in any over place (or be sure to flush it)
  • you do an unnecessary dup2(fds[0],STDIN_FILENO); because you directly read on the other end of the pipe (which is better)
  • you never verify that sz = (pairs*2)+1 is smaller than sizeof(buf)

Edit : In fact, I have just realized that your code could work, even with intermixed printf elsewhere in the application, provided you flush stdout before copying it to fds[1].

So here is a fixed version of your X() function with clearly identified edits (but IMHO you should considered my other suggestions too) :

void X(int pairs,char* expOut)
{
    char buf[256];
    int fds[2];
    char output[300];
    char input[50];

    /* BEGIN EDIT */
    /* first flush stdout */
    fflush(stdout);
    /* END EDIT */

    /* opening pipes */
    pipe(fds);

    /* saving the the given stdout stream */
    int bak = dup(STDOUT_FILENO);

    /* associating Fds[1] pipe with stdout */
    int res=dup2(fds[1],STDOUT_FILENO);

    /* associating Fds[0] pipe with stdin */
    dup2(fds[0],STDIN_FILENO);
    assert(res!=-1);

    /* Call To function Y: function combParenthesis is a recursive function,
     which prints out some strings couple of time */
    combParenthesis(pairs) ;


    /* closing stdout FD stream */
    close(fds[1]);
    fflush(stdout);

    /* restoring the old stdout stream */
    dup2(bak, 1);
    close(bak);

    /* opening, stdin stream for reading */
    FILE *ofp = fdopen(fds[0], "r");

    char strs[30][30];
    for (int i=0;i<30;i++) {
        memset(strs[i], 0, 30);

    }

    int i=0;
    if (ofp)
    {
        int sz;
        if((pairs*2)+1 <= 1)
        {
            sz=5;
        }
        else
        {sz = (pairs*2)+1 ;}

        /* read the stream  line by line */
    // EDIT : changed sz with sizeof(buf)-1 - cause: no correct pairs value
        while (fgets(buf, sizeof(buf) - 1,ofp)) {
            printf("\n next string %s", buf);

            i++;
        }

        if (ferror(ofp)) {
            printf("something went wrong in the input to printf");

        }

    }

    /* different ways to flush it out  */
    /* BEGIN EDIT : ALL FLUSHING COMMENTED OUT 
    char c;
    while( (c = fgetc( ofp )) != EOF && c != '\n' );
    fseek(stdin,EOF,SEEK_END);
    fpurge(ofp);
    fclose(ofp);
    fflush(stdin);
    // _flushlbf();
    END EDIT */

    /* close the  fd associated with stdin */
    close(fds[0]);    
}

I never got garbage in what is read and it works even with output redirected to a file, but for a reason I couldn't explain, the order of the messages is not what I would expect. Hope it's not a concern for you

这篇关于当两个管道属于同一进程时,如何冲洗管道以清理C中的缓冲区?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆