禁用分叉进程的stdout缓冲

我在C / C ++中编写了一个代码,用于处理子进程,将stdin / stdout复制到管道末端并调用execvp。

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

问题是子缓冲区是缓冲的。

所以如果子代码看起来像这样:

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

在父进程中,我没有看到“输入任何键:”行 – 只有在程序调用exit(自动刷新stdout缓冲区)或显式调用’flush(stdout)’后才会“刷新”它添加

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

setvbuf(stdout,NULL,_IONBF,0); 就在父进程中调用execvp(…)之前

所以相关代码现在看起来像这样:

 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  #include  #include  #include  #include  #include  #include  #include  #include  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; } 

实际上,经过一段时间的努力,看起来这个问题的唯一解决办法就是让“父”进程伪装成使用OS伪终端API调用的终端。

应该在fork()之前调用’openpty()’,并且在子代码中,他应该调用’login_tty(slave)’,然后奴隶变成stdin / out和stderr。

通过假装到终端,stdout的缓冲自动设置为“行模式”(即遇到\ n时发生刷新)。 父进程应该使用’master’描述符来读取/写入子进程。

修改后的父代码(如果有人需要这个):

 #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  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; } 

在printf之后插入调用fflush(stdout)是不够的?

否则setvbuf应该做的伎俩:

 setvbuf(stdout,NULL,_IOLBF,0);