代码之家  ›  专栏  ›  技术社区  ›  Eran

禁用分叉进程的stdout缓冲

  •  7
  • Eran  · 技术社区  · 14 年前

    一切正常(即,来自stdin/err/out的输出被父进程捕获)

    问题是子stdout被缓冲。

    printf("Enter any key and hit ENTER:\n");
    fgets(line);
    printf("read: %s\n", line);
    exit(0);
    

    在父进程中,我看不到行“输入任何键:”-只在程序调用Exchange(自动刷新StdOutFuffor)或向“FLUH(STDUT)”显式调用之后才会被“刷新”。

    我做了一些研究,并尝试通过添加一个调用来禁用stdout缓冲:

    setvbuf(stdout,空,_IONBF,0);

    所以现在相关的代码如下所示:

    int rc = fork();
    if ( rc == 0 ) {
        // Child process
        if(workingDirectory.IsEmpty() == false) {
            wxSetWorkingDirectory( workingDirectory );
        }
        int stdin_file  = fileno( stdin  );
        int stdout_file = fileno( stdout );
        int stderr_file = fileno( stderr );
    
        // Replace stdin/out with our pipe ends
        dup2 ( stdin_pipe_read,  stdin_file );
        close( stdin_pipe_write );
    
        dup2 ( stdout_pipe_write, stdout_file);
        dup2 ( stdout_pipe_write, stderr_file);
        close( stdout_pipe_read );
    
        setvbuf(stdout, NULL, _IONBF, 0);
    
        // execute the process
        execvp(argv[0], argv);
        exit(0);
    
    }
    

    没有运气。

    有什么想法吗?

    编辑:

    #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>
    
    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 filedes[2];
        int filedes2[2];
    
        // create a pipe
        int d;
        d = pipe(filedes);
        d = pipe(filedes2);
    
        int stdin_pipe_write = filedes[1];
        int stdin_pipe_read  = filedes[0];
    
        int stdout_pipe_write = filedes2[1];
        int stdout_pipe_read  = filedes2[0];
    
        int rc = fork();
        if ( rc == 0 ) {
    
            // Child process
            int stdin_file  = fileno( stdin  );
            int stdout_file = fileno( stdout );
            int stderr_file = fileno( stderr );
    
            // Replace stdin/out with our pipe ends
            dup2 ( stdin_pipe_read,  stdin_file );
            close( stdin_pipe_write );
    
            dup2 ( stdout_pipe_write, stdout_file);
            dup2 ( stdout_pipe_write, stderr_file);
            close( stdout_pipe_read );
    
            setvbuf(stdout, NULL, _IONBF, 0);
    
            // execute the process
            execvp(argv[0], argv);
    
        } else if ( rc < 0 ) {
            perror("fork");
            return;
    
        } else {
            // Parent
            std::string buf;
            read_handle = stdout_pipe_read;
            while(read_from_child(buf)) {
                if(buf.empty() == false) {
                    printf("Received: %s\n", buf.c_str());
                }
                buf.clear();
            }
        }
    }
    
    int main(int argc, char **argv) {
        execute();
        return 0;
    }
    
    3 回复  |  直到 14 年前
        1
  •  10
  •   Eran    14 年前

    实际上,经过一番挣扎之后,似乎解决这个问题的唯一方法是让“父”进程假装成使用OS伪终端API调用的终端。

    一个人应该在fork()之前调用'openpty()',在子代码中,他应该调用'login_tty(slave)'然后slave变成stdin/out和stderr。

    修改后的父代码(以防有人需要此代码):

    #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;
    }
    
        2
  •  2
  •   Flexo - Save the data dump sunny moon    14 年前

    将插入一个调用 fflush(stdout)

    否则,setvbuf应该做到:

    setvbuf(stdout,NULL,_IOLBF,0);
    
        3
  •  0
  •   virusdefender    6 年前