многопоточность — внешний программный ввод C ++

Мне нужно запустить внешнюю программу из приложения C ++. Мне нужен вывод из этой программы (я хочу видеть его, пока программа еще работает), и он также должен получить ввод.

Какой самый лучший и самый элегантный способ перенаправить ввод-вывод? Должен ли он работать в своем собственном потоке? Есть примеры?

Это работает на OSX.

Я реализовал это так:

ProgramHandler::ProgramHandler(std::string prog): program(prog){
// Create two pipes
std::cout << "Created Class\n";
pipe(pipe1);
pipe(pipe2);

int id = fork();

std::cout << "id: " << id << std::endl;

if (id == 0)
{
// In child
// Close current `stdin` and `stdout` file handles
close(fileno(stdin));
close(fileno(stdout));

// Duplicate pipes as new `stdin` and `stdout`
dup2(pipe1[0], fileno(stdin));
dup2(pipe2[1], fileno(stdout));

// We don't need the other ends of the pipes, so close them
close(pipe1[1]);
close(pipe2[0]);

// Run the external program
execl("/bin/ls", "bin/ls");
char buffer[30];
while (read(pipe1[0], buffer, 30)) {
std::cout << "Buf: " << buffer << std::endl;
}
}
else
{
// We don't need the read-end of the first pipe (the childs `stdin`)
// or the write-end of the second pipe (the childs `stdout`)
close(pipe1[0]);
close(pipe2[1]);// Now you can write to `pipe1[1]` and it will end up as `stdin` in the child
// Read from `pipe2[0]` to read from the childs `stdout`
}

}

но в качестве вывода я получаю это:

Созданный класс

id: 84369

id: 0

Я не понимаю, почему он вызывается дважды и почему он не работает с первого раза. Что я делаю / неправильно понимаю.

0

Решение

Если вы используете систему POSIX (например, OSX или Linux), вы должны изучить системные вызовы. pipe, fork, close, dup2 а также exec,

Вы создаете две трубы: одну для чтения из внешнего приложения и одну для записи. Затем вы fork создать новый процесс, а в дочернем процессе вы настроили каналы как stdin а также stdout а затем позвоните exec который заменяет дочерний процесс внешней программой, использующей ваш новый stdin а также stdout файловые дескрипторы. В родительском процессе вы не можете прочитать выходные данные дочернего процесса и записать его входные данные.

В псевдокоде:

// Create two pipes
pipe(pipe1);
pipe(pipe2);

if (fork() == 0)
{
// In child
// Close current `stdin` and `stdout` file handles
close(FILENO_STDIN);
close(FILENO_STDOUT);

// Duplicate pipes as new `stdin` and `stdout`
dup2(pipe1[0], FILENO_STDIN);
dup2(pipe2[1], FILENO_STDOUT);

// We don't need the other ends of the pipes, so close them
close(pipe1[1]);
close(pipe2[0]);

// Run the external program
exec("/some/program", ...);
}
else
{
// We don't need the read-end of the first pipe (the childs `stdin`)
// or the write-end of the second pipe (the childs `stdout`)
close(pipe1[0]);
close(pipe2[1]);

// Now you can write to `pipe1[1]` and it will end up as `stdin` in the child
// Read from `pipe2[0]` to read from the childs `stdout`
}

Читайте справочные страницы системных звонков для получения дополнительной информации о них. Вам также необходимо добавить проверку ошибок, поскольку все эти системные вызовы могут завершиться ошибкой.

3

Другие решения

Ну, есть довольно стандартный способ сделать это. В общем, вы хотели бы разорвать процесс и закрыть стандартный ввод / вывод (fd 0,1) дочернего процесса. Перед разветвлением создайте две трубы, после разветвления закройте стандартный вход и выход дочернего элемента и подключите их к трубе, используя dup.

Псевдокод показывает только одну сторону соединения, я уверен, что вы можете выяснить другую сторону.

int main(){
int fd[2]; // file descriptors
pipe(fd);

// Fork child process
if (fork() == 0){
char buffer [80];
close(1);
dup(fd[1]); // this will take the first free discriptor, the one you just closed.
close(fd[1]); // clean up
}else{
close(0);
dup(fd[0]);
close(fd[0]);
}
return 0;
}

После того, как вы настроили канал и один из родительских потоков ожидает выбора или чего-то еще, вы можете вызвать exec для вашего внешнего инструмента и передать все данные.

1

Основной подход для связи с другой программой в системах POSIX заключается в настройке pipe(), затем fork() ваша программа, close() а также dup() дескрипторы файлов в правильном месте, и, наконец, exec??() желаемый исполняемый файл.

Как только это будет сделано, у вас есть две программы, связанные с подходящими потоками. К сожалению, это не касается какой-либо формы асинхронной обработки двух программ. То есть вполне вероятно, что вы либо захотите получить доступ к созданному файловому дескриптору с помощью подходящих асинхронных и неблокирующих операций (то есть настроить различные файловые дескрипторы как неблокирующие и / или получить к ним доступ только тогда, когда poll() дает результаты, указывающие, что вы можете получить к ним доступ). Если существует только один исполняемый файл, может быть проще управлять им из отдельного потока.

0

Другой подход (и если вы также пишете внешнюю программу) заключается в использовании разделяемой памяти. Нечто подобное (псевдокод)

// create shared memory
int l_shmid = shmget(key, size ,0600 | IPC_CREAT);

if(l_shmid < 0)
ERROR

// attach to shared memory
dataptr* ptr = (dataptr*)shmat(l_shmid, NULL, 0600);

// run external program
pid_t l_pid = fork();

if(l_pid == (pid_t)-1)
{
ERROR

// detach & delete shared mem
shmdt(ptr);
shmctl(l_shmid,
IPC_RMID,
(shmid_ds *)NULL);
return;
}
else if(l_pid == 0)
{
// child:
execl(path,
args,
NULL);
return;
}

// wait for the external program to finish
int l_stat(0);
waitpid(l_pid, &l_stat, 0);

// read from shmem
memset(mydata, ..,..);
memcpy(mydata, ptr, ...);

// detach & close shared mem
shmdt(ptr);
shmctl(l_shmid,
IPC_RMID,
(shmid_ds *)NULL);

Ваша внешняя программа может записывать в общую память аналогичным образом. Нет необходимости в трубах & чтение / запись / выбор и т. д.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector