Invio di comandi a un processo figlio tramite tubo / dup2 in c
Domanda
Sto cercando di scrivere un programma di controllo remoto per OMXPlayer sul mio rasperry PI
Posso ottenere OmxPlayer a funzionare OK in un processo figlio ma non sembra essere in grado di far funzionare i tubi correttamente per inviare effettivamente comandi al processo figlio.
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);
}
}
.
Quando stavo testando con Top e correva il programma, posso vedere l'uscita superiore appaita nella finestra del terminale e posso vedere il comando q nella finestra, ma sembra che sia andato al processo genitore piuttosto che al bambino.Sto facendo qualcosa di sbagliato con i tubi o non è possibile inviare comandi al processo figlio generato?
Ho provato a cambiare la dichiarazione del figlio DUP2 per copiare dal tubo a STDIN
{ // this is the child process
dup2(fd_pipe[0], 0);
.
Ma quindi la parte superiore non riesce a iniziare con un messaggio Tty fallito
Soluzione
Per interagire con un programma che si aspetta di essere in grado di manipolare un terminale, dovresti usare un pseudo TTY. Questo eviterà l'errore 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;
}
.
Nota che forkpty
non è POSIX, ma un'interfaccia disponibile su BSD e Linux Sapori di Unix.
È possibile eseguire top
in modalità batch, ma non è possibile utilizzare un comando per uscire. Devi ucciderlo.
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;
}
.
Sebbene in esecuzione in modalità batch ti consenta di aprire il processo con una chiamata più semplice (ad esempio popen
come suggerisce MUX), a meno che tale chiamata non ritorna l'ID di processo del bambino, non sarà possibile ucciderlo (senza Esecuzione di un pkill
o scavare attraverso la tabella dei processi per trovare il giusto processo figlio da uccidere).
Penso che tu abbia una certa confusione su come usare dup2
. La pagina manuale utilizza i termini oldfd
e newfd
e significa che oldfd
diventerà newfd
. Per illustrare, ecco un semplice programma che reindirizza Stdout e Stderr a un file di registro, chiama una funzione, quindi ripristina lo stdout e Stderr in seguito.
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;
}
.
Come sottolineato MUX, il tuo programma scrive sulla parte sbagliata del tubo. Il comando pipe
restituisce una coppia unidirezionale di descrittori di file, dove ciò che è mai stato scritto su fd_pipe[1]
può essere letto da fd_pipe[0]
.