gpt4 book ai didi

c++ - 禁用 fork 进程的标准输出缓冲

转载 作者:可可西里 更新时间:2023-11-01 18:25:59 30 4
gpt4 key购买 nike

我用 C/C++ 编写了一个代码,它 fork 一个子进程,将标准输入/标准输出复制到管道末端并调用 execvp。

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

问题是子标准输出被缓冲了。

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

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

在父进程中,我没有看到“Enter any key:”这一行——只有在程序调用 exit(自动刷新 stdout 缓冲区)或显式调用“flush(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 <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;
}

最佳答案

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

一个人应该在 fork() 之前调用“openpty()”,在子代码中,他应该调用“login_tty(slave)”,然后从服务器成为标准输入/输出和标准错误。

通过伪装成终端,stdout 的缓冲自动设置为“行模式”(即在遇到\n 时发生刷新)。父进程应使用“主”描述符与子进程进行读/写。

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

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

关于c++ - 禁用 fork 进程的标准输出缓冲,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4057985/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com