Disabling stdout buffering of a forked process

后端 未结 4 1421
一生所求
一生所求 2020-12-17 23:44

I wrote a code in C/C++ which forks a child process, duplicates the stdin/stdout into a pipe ends and calls execvp.

Everything is working fine (i.e. the output from

相关标签:
4条回答
  • 2020-12-18 00:03

    http://lists.gnu.org/archive/html/bug-coreutils/2008-11/msg00164.html

    you can use LD_PRELOAD to setvbuf

    0 讨论(0)
  • 2020-12-18 00:16

    Would inserting a call to fflush(stdout) after the printf not suffice?

    Otherwise setvbuf should do the trick:

    setvbuf(stdout,NULL,_IOLBF,0);
    
    0 讨论(0)
  • 2020-12-18 00:22

    If you ever need to achieve more control over the buffering of the child process, I made a module that expose features of the stdbuf preload trick, see this self answered question of mine:

    C Control buffering of Child process

    0 讨论(0)
  • 2020-12-18 00:23

    Actually, after struggling with it a bit, it seems like the only solution to this problem is by making the 'parent' process pretending to be a terminal using the OS pseudo terminal API calls.

    One should call 'openpty()' before the fork(), and inside the child code, he should call 'login_tty(slave)' the slave is then becoming the stdin/out and stderr.

    By pretending to a terminal, the buffering of stdout is automatically set to 'line mode' (i.e. flush occurs when \n is encountered). The parent should use the 'master' descriptor for readin/writing with the child process.

    The modified parent code (in case anyone will ever need this):

    #include <unistd.h>
    #include <signal.h>
    #include <sys/types.h>
    #include <sys/select.h>
    #include <errno.h>
    #include <sys/wait.h>
    #include <string>
    #include <string.h>
    #include <cstdio>
    #include <pty.h>
    #include <utmp.h>
    static int   read_handle(-1);
    static pid_t pid;
    
    bool read_from_child(std::string& buff) {
        fd_set  rs;
        timeval timeout;
    
        memset(&rs, 0, sizeof(rs));
        FD_SET(read_handle, &rs);
        timeout.tv_sec  = 1; // 1 second
        timeout.tv_usec = 0;
    
        int rc = select(read_handle+1, &rs, NULL, NULL, &timeout);
        if ( rc == 0 ) {
            // timeout
            return true;
    
        } else if ( rc > 0 ) {
            // there is something to read
            char buffer[1024*64]; // our read buffer
            memset(buffer, 0, sizeof(buffer));
            if(read(read_handle, buffer, sizeof(buffer)) > 0) {
                buff.clear();
                buff.append( buffer );
                return true;
            }
    
            return false;
        } else { /* == 0 */
            if ( rc == EINTR || rc == EAGAIN ) {
                return true;
            }
    
            // Process terminated
            int status(0);
            waitpid(pid, &status, 0);
            return false;
        }
    }
    
    void execute() {
        char *argv[] = {"/home/eran/devl/TestMain/Debug/TestMain", NULL};
        int    argc = 1;
    
        int master, slave;
        openpty(&master, &slave, NULL, NULL, NULL);
    
        int rc = fork();
        if ( rc == 0 ) {
            login_tty(slave);
            close(master);
    
            // execute the process
            if(execvp(argv[0], argv) != 0)
                perror("execvp");
    
        } else if ( rc < 0 ) {
            perror("fork");
            return;
    
        } else {
            // Parent
            std::string buf;
            close(slave);
    
            read_handle = master;
            while(read_from_child(buf)) {
                if(buf.empty() == false) {
                    printf("Received: %s", buf.c_str());
                }
                buf.clear();
            }
        }
    }
    
    int main(int argc, char **argv) {
        execute();
        return 0;
    }
    
    0 讨论(0)
提交回复
热议问题