Pregunta

este pregunta anterior he publicado más de mi propio código shell. Mi siguiente paso es la implementación de proceso de fondo plano y la ejecución y esperar adecuadamente de ellos para terminar de modo que no se quedan como "zombies".

Antes de añadir la posibilidad de ejecutarlos en el fondo, todos los procesos se ejecutan en segundo plano. Y por eso, simplemente se llama a wait (NULL) después de ejecutar cualquier proceso con execvp (). Ahora, puedo comprobar el carácter '&' como último argumento y si está allí, ejecutar el proceso en segundo plano por no llamar a wait (NULL) y el proceso se puede ejecutar alegremente en el fondo a que estoy de volver a mi concha.

Esto es todo funciona correctamente (creo), el problema ahora, es que yo también necesito llamar a wait () (o waitpid ()?) De alguna manera para que el proceso en segundo plano no se queda "zombi". Ese es mi problema, no estoy seguro de cómo hacer que ...

Creo que tengo para manejar SIGCHLD y hacer algo allí, pero todavía tengo que entender completamente cuando se envía la señal SIGCHLD porque he intentado añadir también espera (NULL) para childSignalHandler (), pero no funcionó porque como pronto como ejecuta un proceso en segundo plano, la función childSignalHandler () fue llamado y, en consecuencia, la espera (NULL), lo que significa que no podía hacer nada con mi concha hasta que el proceso de "fondo" terminó. Que no se ejecuta en segundo plano más debido a la espera en el manejador de la señal.

¿Qué me falta en todo esto?

Una última cosa, parte de este ejercicio también tengo que imprimir las variaciones de estado de procesos, como la terminación del proceso. Por lo tanto, cualquier idea sobre que es también muy apreciado.

Este es mi código completo en el momento:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wait.h>
#include <signal.h>
#include <sys/types.h>

#include "data.h" // Boolean typedef and true/false macros


void childSignalHandler(int signum) {
    //
}

int main(int argc, char **argv) {
    char bBuffer[BUFSIZ], *pArgs[10], *aPtr = NULL, *sPtr;
    bool background;
    ssize_t rBytes;
    int aCount;
    pid_t pid;

    //signal(SIGINT, SIG_IGN);

    signal(SIGCHLD, childSignalHandler);

    while(1) {
        write(1, "\e[1;31mmyBash \e[1;32m# \e[0m", 27);
        rBytes = read(0, bBuffer, BUFSIZ-1);

        if(rBytes == -1) {
            perror("read");
            exit(1);
        }

        bBuffer[rBytes-1] = '\0';

        if(!strcasecmp(bBuffer, "exit")) {
            exit(0);
        }

        sPtr = bBuffer;
        aCount = 0;

        do {
            aPtr = strsep(&sPtr, " ");
            pArgs[aCount++] = aPtr;
        } while(aPtr);

        background = FALSE;

        if(!strcmp(pArgs[aCount-2], "&")) {
            pArgs[aCount-2] = NULL;
            background = TRUE;
        }

        if(strlen(pArgs[0]) > 1) {
            pid = fork();

            if(pid == -1) {
                perror("fork");
                exit(1);
            }

            if(pid == 0) {
                execvp(pArgs[0], pArgs);
                exit(0);
            }

            if(!background) {
                wait(NULL);
            }
        }
    }

    return 0;
}
¿Fue útil?

Solución

Hay varias opciones para waitpid() para ayudarle (citas del estándar POSIX):

  

WCONTINUED

     

La función waitpid () informará el estado de cualquier proceso hijo especificado por pid continuo cuyo estado no se ha informado ya que viene de una parada de control de trabajos.

     

WNOHANG

     

La función waitpid () no suspenderá la ejecución del subproceso de llamada si el estado no es inmediatamente disponible para uno de los procesos secundarios especificados por pid.

En particular, WNOHANG le permitirá ver si hay cadáveres para recoger sin causar su proceso de bloquear la espera de un cadáver.

  

Si el proceso de llamada tiene SA_NOCLDWAIT establecer o tiene SIGCHLD establece en SIG_IGN, y el proceso no tiene unwaited-para los niños que se transformaron en los procesos zombi, el subproceso de llamada se bloqueará hasta que todos los niños del proceso que contienen el subproceso de llamada dar por terminado, y esperar () y waitpid () deberá fallar y asignar a errno [ECHILD].

Es probable que no quiere estar ignorando SIGCHLD, etc, y su manejador de señales probablemente debería estar fijándose una bandera de decirle a su bucle principal "Vaya, no hay niño muerto - ir a recoger el cadáver!"

. señales

El SIGCONT y SIGSTOP también serán de interés para usted -. que se utilizan para reiniciar y detener un proceso hijo, respectivamente (en este contexto, en todo caso)

te recomiendo mirar el libro de Rochkind o un libro de Stevens - que cubren estos temas en detalle

.

Otros consejos

Esto debería empezar. La principal diferencia es que se deshizo del manipulador niño y añadió waitpid en el bucle principal con algunos comentarios. Probado y de trabajo, pero obviamente necesita más TLC.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <wait.h>
#include <signal.h>
#include <sys/types.h>

int main(int argc, char **argv) {
        char bBuffer[BUFSIZ], *pArgs[10], *aPtr = NULL, *sPtr;
        int background;
        ssize_t rBytes;
        int aCount;
        pid_t pid;
        int status;
        while(1) {
                pid = waitpid(-1, &status, WNOHANG);
                if (pid > 0)
                        printf("waitpid reaped child pid %d\n", pid);
                write(1, "\e[1;31mmyBash \e[1;32m# \e[0m", 27);
                rBytes = read(0, bBuffer, BUFSIZ-1);
                if(rBytes == -1) {
                        perror("read");
                        exit(1);
                }
                bBuffer[rBytes-1] = '\0';
                if(!strcasecmp(bBuffer, "exit")) 
                        exit(0);
                sPtr = bBuffer;
                aCount = 0;
                do {
                        aPtr = strsep(&sPtr, " ");
                        pArgs[aCount++] = aPtr;
                } while(aPtr);
                background = (strcmp(pArgs[aCount-2], "&") == 0);
                if (background)
                        pArgs[aCount-2] = NULL;
                if (strlen(pArgs[0]) > 1) {
                        pid = fork();
                        if (pid == -1) {
                                perror("fork");
                                exit(1);
                        } else if (pid == 0) {
                                execvp(pArgs[0], pArgs);
                                exit(1);
                        } else if (!background) {
                                pid = waitpid(pid, &status, 0);
                                if (pid > 0)
                                        printf("waitpid reaped child pid %d\n", pid);
                        }
                }
        }
        return 0;
}

EDIT: Adición de vuelta en el manejo de señales no es difícil con waitpid() usando WNOHANG. Es tan sencillo como mover el material waitpid() desde la parte superior del bucle en el manejador de la señal. Usted debe ser consciente de dos cosas, sin embargo:

En primer lugar, incluso los procesos de "primer plano", enviará SIGCHLD. Dado que sólo puede haber un proceso de primer plano se puede simplemente almacenar el pid primer plano (valor de retorno de los padres de fork()) de manera visible variable al manejador de la señal si se quiere hacer un manejo especial de los conocimientos adquiridos frente a fondo.

En segundo lugar, usted está haciendo actualmente el bloqueo de E / S en la entrada estándar (el read() en la parte superior del bucle principal). Usted es muy probable que sea bloqueada en read() cuando SIGCHLD se produce, lo que resulta en una llamada al sistema interrumpida. Dependiendo del sistema operativo que se reinicie la llamada al sistema de forma automática, o puede enviar una señal de que debe manejar.

Es posible utilizar:

if(!background)
    pause();

De esta manera, los bloques de proceso hasta que se recibe la señal SIGCHLD, y el manejador de la señal va a hacer las cosas de espera.

En lugar de utilizar una variable global, pensé en una solución diferente:

if(!background) {
    signal(SIGCHLD, NULL);

    waitpid(pid, NULL, 0);

    signal(SIGCHLD, childSignalHandler);
}

Si estoy ejecutando un proceso en primer plano "eliminar" el controlador para SIGCHLD por lo que no se consiga llamar. Entonces, después de waitpid (), configurar el controlador de nuevo. De esta manera, será manejada sólo los procesos en segundo plano.

¿Cree que hay algo malo con esta solución?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top