Home > Back-end >  Strange behavior when reading data from pipe
Strange behavior when reading data from pipe

Time:05-29

After I carefully read the manual(i.e. man 2 pipe), I found a demo code snippet:

       #include <sys/wait.h>
       #include <stdio.h>
       #include <stdlib.h>
       #include <unistd.h>
       #include <string.h>

       int
       main(int argc, char *argv[])
       {
           int pipefd[2];
           pid_t cpid;
           char buf;

           if (argc != 2) {
            fprintf(stderr, "Usage: %s <string>\n", argv[0]);
            exit(EXIT_FAILURE);
           }

           if (pipe(pipefd) == -1) {
               perror("pipe");
               exit(EXIT_FAILURE);
           }

           cpid = fork();
           if (cpid == -1) {
               perror("fork");
               exit(EXIT_FAILURE);
           }

           if (cpid == 0) {    /* Child reads from pipe */
               close(pipefd[1]);          /* Close unused write end */

               while (read(pipefd[0], &buf, 1) > 0)
                   write(STDOUT_FILENO, &buf, 1);

               write(STDOUT_FILENO, "\n", 1);
               close(pipefd[0]);
               _exit(EXIT_SUCCESS);

           } else {            /* Parent writes argv[1] to pipe */
               close(pipefd[0]);          /* Close unused read end */
               write(pipefd[1], argv[1], strlen(argv[1]));
               close(pipefd[1]);          /* Reader will see EOF */
               wait(NULL);                /* Wait for child */
               exit(EXIT_SUCCESS);
           }
       }

I made some small modifications to the code snippet above(i.e.: adding sleep in the parent process & printing out the size returned by read in the child process).

#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <string>
#include <thread>
#include <array>
#include <iostream>

int
main(int argc, char *argv[])
{
    int pipefd[2];
    pid_t cpid;
    std::array<char, 1024> buf;
    
    if (pipe(pipefd) == -1) {
        perror("pipe");
        exit(EXIT_FAILURE);
    }
    cpid = fork();
    if (cpid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    if (cpid == 0) {    /* Child reads from pipe */
        close(pipefd[1]);          /* Close unused write end */
        int size;
        while (size = read(pipefd[0], buf.data(), 1) > 0)
             std::cout << size << std::endl;
        write(STDOUT_FILENO, "\n", 1);
        close(pipefd[0]);
        _exit(EXIT_SUCCESS);
    } else {            /* Parent writes argv[1] to pipe */
        close(pipefd[0]);          /* Close unused read end */
        std::string str{"hello world\n"};
        write(pipefd[1], str.c_str(), str.size());
        std::this_thread::sleep_for(std::chrono::seconds(5));
        close(pipefd[1]);          /* Reader will see EOF */
        wait(NULL);                /* Wait for child */
        exit(EXIT_SUCCESS);
    }
}

Here is the output of the code snippet above:

1
1
1
1
1
1
1
1
1
1
1
1

It seems all right so far. What makes confused is that when I increase the number of bytes to read.The output is very strange.Here is the said code snippet:

#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <string>
#include <thread>
#include <array>
#include <iostream>

int
main(int argc, char *argv[])
{
    int pipefd[2];
    pid_t cpid;
    std::array<char, 1024> buf;
    
    if (pipe(pipefd) == -1) {
        perror("pipe");
        exit(EXIT_FAILURE);
    }
    cpid = fork();
    if (cpid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    if (cpid == 0) {    /* Child reads from pipe */
        close(pipefd[1]);          /* Close unused write end */
        int size;
        while (size = read(pipefd[0], buf.data(), buf.size()) > 0)
             std::cout << size << std::endl;
        write(STDOUT_FILENO, "\n", 1);
        close(pipefd[0]);
        _exit(EXIT_SUCCESS);
    } else {            /* Parent writes argv[1] to pipe */
        close(pipefd[0]);          /* Close unused read end */
        std::string str{"hello world\n"};
        write(pipefd[1], str.c_str(), str.size());
        std::this_thread::sleep_for(std::chrono::seconds(5));
        close(pipefd[1]);          /* Reader will see EOF */
        wait(NULL);                /* Wait for child */
        exit(EXIT_SUCCESS);
    }
}

Here is the strange output:

1
//new line, and no more output indeed.

Maybe I need to know which types of buffering the file descriptors returned by pipe use.And I can't find any useful information on manual(i.e. man 2 pipe).

Could anybody shed some light on this matter?

CodePudding user response:

> has a higher operator precedence than =. Thus,

size = read(pipefd[0], buf.data(), buf.size()) > 0

is equivalent to

size = (read(pipefd[0], buf.data(), buf.size()) > 0)

The first iteration would appear to consume all the data, thus read should return 12. 12 > 0 is true, and true is implicitly converted to the integer value 1.

The next iteration, read should return a non-positive number, ending the loop.

When the read size was 1, its expected return value was indistinguishable from the result of 1 > 0.

Place the parenthesis around the assignment.

(size = read(pipefd[0], buf.data(), buf.size())) > 0
  • Related