HOWTO · C

Usa la funzione waitpid in C

Questo articolo introduce come utilizzare la funzione waitpid in C

In questa pagina

Usa waitpid quando un processo padre deve raccogliere lo stato di un processo figlio, sia per un figlio specifico sia per uno qualsiasi tra quelli idonei. Gli argomenti selezionano il figlio e i cambiamenti di stato da segnalare; lo stato restituito va decodificato con le macro di <sys/wait.h>, senza trattarlo direttamente come un codice di uscita.

Usa waitpid per attendere un processo figlio specifico

Un processo può creare un figlio con fork. Il figlio prosegue dall’istruzione successiva alla chiamata, mentre il padre riceve l’ID del processo figlio. Per rendere disponibile lo stato di terminazione e recuperare le risorse del processo, il padre dovrebbe raccogliere il figlio terminato con una funzione della famiglia wait.

La chiamata ha la forma waitpid(pid, &status, options). Un valore positivo di pid seleziona quel processo figlio. -1 seleziona un figlio qualsiasi il cui stato corrisponda alle opzioni: è la selezione usata da wait. Il valore 0 seleziona i figli appartenenti al gruppo di processi del chiamante; un valore minore di -1 seleziona i figli del gruppo il cui ID è il valore assoluto di pid. Quindi waitpid è utile per indirizzare l’attesa a un figlio specifico, ma non è l’unica funzione della famiglia wait che consente di attendere i figli.

Il secondo argomento punta a un intero nel quale il kernel memorizza uno stato codificato quando viene segnalato un cambiamento di stato del figlio. Se ti serve quello stato, passa un puntatore valido. Il terzo argomento è una maschera di bit che specifica le opzioni. Con 0, la chiamata attende che il figlio selezionato termini. Se non è pronto alcun cambiamento di stato corrispondente, la chiamata si blocca.

Scegli quali eventi dei processi figli segnalare

WNOHANG rende la chiamata non bloccante: se nessun figlio selezionato presenta un cambiamento di stato da segnalare, waitpid restituisce subito 0. Il processo padre può così svolgere altre attività e controllare di nuovo in seguito. Un valore di ritorno positivo è l’ID del figlio il cui stato è stato scritto. WUNTRACED segnala anche un figlio che si è arrestato e il cui arresto non era ancora stato segnalato; WCONTINUED segnala un figlio che ha ripreso l’esecuzione dopo un arresto. Queste opzioni specificano quali cambiamenti di stato possono soddisfare la chiamata: una singola chiamata non osserva tutte le transizioni future. Puoi combinare le opzioni con l’operatore OR bit a bit, per esempio WUNTRACED | WCONTINUED.

Decodifica lo stato restituito da waitpid

L’intero che rappresenta lo stato è codificato e va verificato con le macro corrispondenti. WIFEXITED(status) indica una terminazione normale; in tal caso, WEXITSTATUS(status) estrae il codice di uscita. WIFSIGNALED(status) indica una terminazione causata da un segnale e WTERMSIG(status) restituisce quel segnale. Se chiedi di segnalare i figli arrestati, WIFSTOPPED(status) e WSTOPSIG(status) identificano l’arresto. Se la funzionalità è supportata e richiesta, WIFCONTINUED(status) indica che un figlio ha ripreso l’esecuzione. WCOREDUMP è disponibile solo su alcuni sistemi: se la usi, proteggine l’impiego con una direttiva di controllo del preprocessore. Queste macro sono dichiarate in <sys/wait.h>.

Non chiamare WEXITSTATUS se WIFEXITED non è vero, né WTERMSIG se WIFSIGNALED non è vero. L’intero non è di per sé il codice di uscita del figlio; usare le macro evita di dipendere dalla disposizione dei bit, specifica dell’implementazione.

Gestisci i valori restituiti e gli errori

Un risultato di waitpid maggiore di zero identifica il figlio di cui è stato raccolto lo stato. Un risultato pari a zero significa che non è pronto alcuno stato corrispondente e si verifica con WNOHANG. Un risultato -1 indica un errore: controlla errno per individuarne la causa. Per esempio, ECHILD significa che non esiste un figlio corrispondente alla selezione, mentre EINTR significa che un segnale intercettato ha interrotto l’attesa. Se il programma deve continuare ad attendere dopo EINTR, ripeti la chiamata; altrimenti gestisci l’interruzione secondo la politica dell’applicazione per i segnali. Anche una chiamata senza WNOHANG può bloccarsi a tempo indeterminato se il figlio selezionato continua a essere in esecuzione e non si verifica alcun cambiamento di stato richiesto.

Esegui un esempio completo con un processo figlio

Questo esempio POSIX crea un figlio, attende specificamente che termini, ripete l’attesa se un segnale la interrompe e stampa il codice di uscita decodificato. Usa fork e waitpid e non dipende da un programma separato né da input interattivo. Il codice è pensato per un ambiente POSIX con un compilatore C e non viene presentato come portabile secondo lo standard ISO C. Le prove di esecuzione fornite registrano una conclusione riuscita, con codice di uscita 0 e l’output mostrato qui sotto. L’etichetta visibile nell’output resta in inglese, perché il relativo blocco è mantenuto identico all’originale.

Esegui questo esempio:

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

int main(void) {
    pid_t child = fork();
    if (child == -1) {
        perror("fork");
        return EXIT_FAILURE;
    }

    if (child == 0) {
        _exit(7);
    }

    int status;
    pid_t result;
    do {
        result = waitpid(child, &status, 0);
    } while (result == -1 && errno == EINTR);

    if (result == -1) {
        perror("waitpid");
        return EXIT_FAILURE;
    }

    if (WIFEXITED(status)) {
        printf("child exit status: %d\n", WEXITSTATUS(status));
    } else if (WIFSIGNALED(status)) {
        printf("child terminated by signal: %d\n", WTERMSIG(status));
    }

    return EXIT_SUCCESS;
}
child exit status: 7