Я написал код на 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);
В родительском процессе я не вижу строки «Enter any key:» - она будет «очищена» только после того, как программа вызовет exit (который автоматически очищает буфер stdout) или явный вызов flush (stdout ) 'добавлено
Я провел некоторое исследование и попытался добавить вызов, чтобы отключить буферизацию стандартного вывода, добавив вызов:
setvbuf (стандартный вывод, 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);
}
Без удачи.
Есть идеи?
EDIT:
Вот пример родительского кода, единственное, что нужно изменить, - это путь к дочернему исполняемому файлу:
#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;
}