отправка команд дочернему процессу через Pipe/dup2 в C
Вопрос
Я пытаюсь написать программу удаленного управления для omxplayer на моем Rasperry Pi.
Я могу заставить omxplayer нормально работать в дочернем процессе, но, похоже, я не могу заставить каналы работать правильно, чтобы фактически отправлять команды дочернему процессу.
int fd_pipe[2];
pipe (fd_pipe);
while(1) { // main accept() loop
printf("server: got connection from %s\n", s);
/* Attempt to fork and check for errors */
if( (pid=fork()) == -1){
fprintf(stderr,"Fork error. Exiting.\n"); /* something went wrong */
exit(1);
}
if (pid==0)
{ // this is the child process
dup2(0, fd_pipe[0]);
close(fd_pipe[1]);
if(execl("/usr/bin/top","top",NULL) == -1){
fprintf(stderr,"execl Error!");
exit(1);
}
//try and send command here
write(fd_pipe[0], "q", 1);
exit(0);
} else {
close(new_fd); // parent doesn't need this
dup2(1, fd_pipe[1]);
//try and send here too
write(fd_pipe[0], "q", 1);
}
}
Когда я тестировал с помощью top и запускал программу, я вижу верхний вывод в окне терминала и вижу в окне команду q, но похоже, что она идет к родительскому процессу, а не к дочернему процессу.Я делаю что-то не так с каналами или невозможно отправлять команды порожденному дочернему процессу?
Я попытался изменить дочерний оператор dup2, чтобы скопировать его из канала в стандартный ввод.
{ // this is the child process
dup2(fd_pipe[0], 0);
Но затем top не запускается с ошибкой получения сообщения tty
Решение
Чтобы взаимодействовать с программой, которая ожидает возможности манипулировать терминалом, вам следует использовать псевдотерминал.Это позволит избежать failed tty get
ошибка.
/*...*/
#include <pty.h>
void do_child () {
if (execlp("top", "top", (const char *)0) < 0) {
perror("exec top");
exit(EXIT_FAILURE);
}
/* NOTREACHED */
}
void do_parent (int fd, pid_t p) {
sleep(5);
char r;
write(fd, "q", 1);
while (read(fd, &r, 1) > 0) { write(1, &r, 1); }
waitpid(p, 0, 0);
close(fd);
}
int main () {
int fd;
pid_t p = forkpty(&fd, 0, 0, 0);
switch (p) {
case 0: do_child();
/* NOTREACHED */
case -1: perror("forkpty");
exit(EXIT_FAILURE);
default: break;
}
do_parent(fd, p);
return 0;
}
Обратите внимание, что forkpty
это не POSIX, а интерфейс, доступный в версиях UNIX BSD и Linux.
Вы можете выполнить top
в пакетном режиме, но для выхода нельзя использовать команду.Вы должны убить его.
void do_child () {
if (execlp("top", "top", "-b", (const char *)0) < 0) {
perror("exec top");
exit(EXIT_FAILURE);
}
/* NOT REACHED */
}
void do_parent (pid_t p) {
sleep(5);
if (kill(p, SIGINT) < 0) {
perror("kill");
exit(EXIT_FAILURE);
}
waitpid(p, 0, 0);
}
int main () {
pid_t p;
switch ((p = fork())) {
case 0: do_child();
case -1: perror("fork"); exit(EXIT_FAILURE);
default: break;
}
do_parent(p);
return 0;
}
Хотя запуск в пакетном режиме позволит вам открыть процесс более простым вызовом (например, popen
как предполагает mux), если этот вызов не вернет идентификатор дочернего процесса, вы не сможете его убить (без выполнения pkill
, или покопайтесь в таблице процессов, чтобы найти нужный дочерний процесс, который нужно уничтожить).
Я думаю, у вас есть некоторая путаница в том, как использовать dup2
.На странице руководства используются термины oldfd
и newfd
, и это означает, что oldfd
станет newfd
.Для иллюстрации приведем простую программу, которая перенаправляет стандартный вывод и стандартный вывод stderr в файл журнала, вызывает функцию, а затем восстанавливает стандартный вывод и стандартный вывод stderr.
void do_something () {
fputs("Error message\n", stderr);
puts("This is regular output.");
fputs("Error message\n", stderr);
puts("This is regular output.");
}
int main () {
int fd = creat("/tmp/output.log", 0664);
int outfd = dup(fileno(stdout));
int errfd = dup(fileno(stderr));
fflush(stdout);
fflush(stderr);
dup2(fd, fileno(stdout));
dup2(fd, fileno(stderr));
setlinebuf(stdout);
do_something();
fflush(stdout);
fflush(stderr);
dup2(outfd, fileno(stdout));
dup2(errfd, fileno(stderr));
close(outfd);
close(errfd);
fputs("Error to the screen\n", stderr);
puts("Regular output to screen");
fputs("Error to the screen\n", stderr);
puts("Regular output to screen");
return 0;
}
Как указал Mux, ваша программа записывает не в тот конец канала.А pipe
команда возвращает однонаправленную пару файловых дескрипторов, где все, что записано, fd_pipe[1]
можно прочитать из fd_pipe[0]
.