2014-12-05 23 views
7

Ho una funzione che si biforca un processo, descrittori di file duplicati per buffer di ingresso e di uscita, e poi corre execl su un comando trasmesso in via di una stringa denominata cmd:Catturare codice di stato di uscita del processo figlio

static pid_t 
c2b_popen4(const char* cmd, int pin[2], int pout[2], int perr[2], int flags) 
{ 
    pid_t ret = fork(); 

    if (ret < 0) { 
     fprintf(stderr, "fork() failed!\n"); 
     return ret; 
    } 
    else if (ret == 0) { 
     /*                                                                             
      Assign file descriptors to child pipes (not shown)...                                                                        
     */ 
     execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); 
     fprintf(stderr, "execl() failed!\n"); 
     exit(EXIT_FAILURE); 
    } 
    else { 
     /*                                                                             
      Close parent read and write pipes (not shown)...                                                                        
     */ 
     return ret; 
    } 
    return ret; 
} 

Ciascuna delle istanze cmd elabora correttamente i miei dati, a condizione che i miei input di test siano corretti.

Quando cattivo dati vengono passati ad un processo figlio, il mio programma genitore verrà eseguito fino al completamento e uscire con un codice di stato non-errore 0.

Se ho volutamente messo in cattive di ingresso - per cercare di proposito per ottenere una delle istanze cmd fallire in modo atteso - Mi piacerebbe sapere come acquisire lo stato di uscita di quello cmd in modo che possa emettere il codice di stato di errore corretto dal programma principale, prima della conclusione.

Come si fa generalmente?

risposta

11

si può ottenere lo stato di uscita del bambino tramite il primo argomento di wait(), o il secondo argomento di waitpid(), e quindi utilizzando le macro WIFEXITED e WEXITSTATUS con esso.

Ad esempio:

pid_t ret = c2b_popen4("myprog", pin, pout, perr, 0); 

if (ret > 0) { 
    int status; 

    if (waitpid(ret, &status, 0) == -1) { 
     perror("waitpid() failed"); 
     exit(EXIT_FAILURE); 
    } 

    if (WIFEXITED(status)) { 
     int es = WEXITSTATUS(status); 
     printf("Exit status was %d\n", es); 
    } 
} 

un esempio semplificato di lavoro:

failprog.c:

int main(void) { 
    return 53; 
} 

shellex.c:

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

int main(void) 
{ 
    pid_t p = fork(); 
    if (p == -1) { 
     perror("fork failed"); 
     return EXIT_FAILURE; 
    } 
    else if (p == 0) { 
     execl("/bin/sh", "bin/sh", "-c", "./failprog", "NULL"); 
     return EXIT_FAILURE; 
    } 

    int status; 
    if (waitpid(p, &status, 0) == -1) { 
     perror("waitpid failed"); 
     return EXIT_FAILURE; 
    } 

    if (WIFEXITED(status)) { 
     const int es = WEXITSTATUS(status); 
     printf("exit status was %d\n", es); 
    } 

    return EXIT_SUCCESS; 
} 

uscita :

[email protected]:~/src/sandbox$ ./shellex 
exit status was 53 
[email protected]:~/src/sandbox$ 

waitpid() bloccherà finché il processo con le uscite ID processo forniti. Dato che chiami la tua funzione con un nome popen() e passa ad essa, presumibilmente il processo figlio non si interrompe rapidamente, quindi probabilmente non sarebbe il posto giusto per controllarlo, se la chiamata è riuscita. È possibile passare WNOHANG come terzo parametro a waitpid() per verificare se il processo è terminato e restituire 0 se il bambino non è ancora uscito, ma è necessario fare attenzione a quando si esegue questa operazione, poiché non si ottiene alcuna garanzia su quale processo verrà eseguito quando. Se chiami WNOHANG con WNOHANG immediatamente dopo il ritorno dallo c2b_popen4(), è possibile che venga restituito 0 prima che il processo figlio abbia avuto la possibilità di eseguirlo e terminarlo con un codice di errore e farlo apparire come se l'esecuzione fosse andata a buon fine quando stava per non essere riuscito.

Se il processo non muore subito, avrai problemi di lettura e la scrittura ai vostri tubi, quindi una possibilità potrebbe essere quella di verificare waitpid() se si ottiene un errore dal primo tentativo di farlo, per verificare se il read() o write() non funziona perché il processo figlio è morto. Se ciò risulta vero, puoi recuperare lo stato di uscita e uscire dal tuo programma generale.

Ci sono altre possibili strategie, tra cui la cattura del segnale SIGCHLD, dal momento che verrà sollevato ogni volta che uno dei tuoi processi figlio muore. Per esempio, sarebbe corretto chiamare _exit() direttamente dal gestore del segnale, dopo aver atteso il processo figlio (chiamare anche waitpid() in un gestore di segnale) e ottenere il suo stato di uscita.

+1

Quando eseguo il primo blocco di codice (l'esempio "ad esempio"), il processo si blocca sulla chiamata 'waitpid()'. –

+1

@AlexReynolds: Ecco come funziona per impostazione predefinita, vedere l'aggiornamento per rispondere. Avrai bisogno di un qualche tipo di strategia per rilevare e quindi affrontare la possibilità che i processi figli finiscano presto. –

Problemi correlati