Je suis passé toute une demi-journée, mais ne pouvait toujours pas comprendre, pourquoi le tiret est lancé avec appel execl
devient juste un zombie.
Voici un cas de test minimal: je ne fais que fourrer un enfant, faire un duplicata de descripteurs std [in, out, err] et lancer le sh .
#include <cstdio> #include <fcntl.h> #include <cssortingng> #include <stdlib.h> #include <cerrno> #include <unistd.h> int main() { int pipefd[2]; enum { STDOUT_TERM = 0, STDIN_TERM = 1 }; if (pipe(pipefd) == -1) { //make a pipe perror("pipe"); return 0; } pid_t pid = fork(); if (pid == 0) {// Child dup2(pipefd[STDIN_TERM], STDIN_FILENO); dup2(pipefd[STDOUT_TERM], STDOUT_FILENO); dup2(pipefd[STDOUT_TERM], STDERR_FILENO); execl("/bin/sh","sh", (char*)NULL); // Nothing below this line should be executed by child process. If so, print err perror("For creating a shell process"); exit(1); } __asm("int3"); puts("Child launched"); }
Quand je le lance dans un débogueur, et à la ligne avec le point d'arrêt (au-dessus de l'appel puts()
) regarde la variable pid , puis regarde le process correspondant avec ps, chaque fois que j'obtiens quelque chose comme
2794 pts/10 00:00:00 sh <defunct>
C'est à dire un zombie
Vous quittez un zombie, sortingvialement, parce que vous n'avez pas wait
le process de votre enfant.
Votre shell se ferme immédiatement parce que vous avez configuré son STDIN de manière absurde. pipe
renvoie un canal de communication à sens unique. Vous write
à pipefd[1]
et read
le de pipefd[0]
. Vous avez fait un buch d'appels dup2
qui amènent le shell à essayer de lire (STDIN) à partir de l'extrémité d'écriture du tuyau.
Une fois que vous avez échangé les nombres dans votre énumération, vous obtenez le shell assis pour toujours sur la read
. Ce n'est probablement pas ce que vous voulez non plus, mais c'est à peu près tout ce à quoi vous pouvez vous attendre lorsque vous avez un obus dirigé vers lui-même.
En supposant que vous tentez d'utiliser le shell à partir de votre process parent, vous devez appeler deux fois pipe
(et les deux dans le parent): l'un des tuyaux que vous écrivez (et le shell lit sur, stdin) et l'autre le shell écrit dans (stdout / stderr) et vous lisez. Ou si vous voulez, utilisez socketpair
place.
Vous devez attraper le signal SIGCHLD et "récolter" le process zombie avec un appel système wait()
. Ajout presque minimal de code à votre programme pour append une fonction de gestionnaire de signal, et le définir comme le gestionnaire SIGCHLD ressemble à ceci:
#include <cstdio> #include <fcntl.h> #include <cssortingng> #include <stdlib.h> #include <cerrno> #include <unistd.h> #include <signal.h> #include <sys/types.h> #include <sys/wait.h> void sighandler(int signal_number); void sighandler(int signo) { if (signo == SIGCHLD) { int status; (void)wait(&status); } } int main() { int pipefd[2]; enum { STDOUT_TERM = 0, STDIN_TERM = 1 }; signal(SIGCHLD, sighandler); pid_t pid = fork();
Vous devriez presque certainement vérifier l'état de return de l'appel système signal()
et examiner l'état de sortie de l'enfant (valeur du status
dans le code du gestionnaire de signaux).