Search code examples
c++linuxpipeposix-select

multiplexing unnamed pipe and other file descriptors using select


I'm trying to multiplex an unnamed pipe with some other file descriptors. The problem is that the pipe file descriptor always appears in the result of select. In other words the event-loop reads from pipe for infinite times. Here is a metaphor of what I want to do, and what actually happens.

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


using namespace std;
int main(){
    fd_set master;    // master file descriptor list
    fd_set read_fds;  // temp file descriptor list for select() 
    FD_ZERO(&master);    // clear the master and temp sets
    FD_ZERO(&read_fds);
    int fdmax;        // maximum file descriptor number
    int pfd[2];
    if(pipe(pfd)!=0) {cout<<"Unable to create a pipe.\n",exit(1);};

    FD_SET(0, &master);
    FD_SET(pfd[0],&master);

    fdmax=pfd[0];

    if(fork()){//Parent


        for (;;){
            read_fds = master; // copy it
            if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
                perror("select");
                exit(4);
            }
            for(int i = 0; i <= fdmax; i++) {
                if (FD_ISSET(i, &read_fds)) {
                    int n;
                    char buff[200];

                    if (i==pfd[0]){
                        close(pfd[1]);
                        n=read(pfd[0],buff,sizeof(buff));
                        buff[n]=0;
                        cout<<"Read from pipe:"<<buff<<endl;
                    }else if(i==0){
                        n=read(0,buff,sizeof(buff));
                        buff[n]=0;
                        cout<<"Read from std:"<<buff<<endl;
                    }
                }
            }

        }
    }else{//Child
        usleep(50000);
        char buff[200]="This is a simple sample.";
        close(pfd[0]);
        write(pfd[1],buff,sizeof(buff));
        close(pfd[1]);
        exit(0);
    }
}

Solution

  • First of all, the read() call can read less than number of bytes specified in the last argument ant id does not automatically append zero-byte terminator, so your receiving code can easily access uninitialized memory in buff[] and after it (if there is no zero byte). You need to check return value when calling read and use only so many bytes from buffer.

    Then, the select call returns when any file descriptor in the readfds set won't block on subsequent read. Which includes end-of-file condition. This likely happens in your case, when the forked process closes its fd. See this SO question too.

    Could it be the reason for the problem you encounter? Checking return value when calling read make this clear to you, as read returns zero if-and-only-if the fd reached end of file.

    Last detail – it does not make much sense to close pfd[1] only after pfd[0] is returned in the readfds. You should close it immediately after the fork, so it remains open in the child process only if you have no use for it in the parent process.