How to flush pipes to clean the buffer in C, when

2019-07-18 05:52发布

问题:

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]);    
}

回答1:

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 linux pipe flush