Come attendere il completamento di tutti i processi figlio chiamati da fork ()?
-
07-07-2019 - |
Domanda
Sto eseguendo il fork di una serie di processi e desidero misurare il tempo necessario per completare l'intera attività, ovvero quando vengono completati tutti i processi a fork. Si prega di avvisare come attendere il completamento del processo parent fino al termine di tutti i processi child? Voglio assicurarmi di interrompere il timer al momento giusto.
Ecco come codice che uso:
#include <iostream>
#include <string>
#include <fstream>
#include <sys/time.h>
#include <sys/wait.h>
using namespace std;
struct timeval first, second, lapsed;
struct timezone tzp;
int main(int argc, char* argv[])// query, file, num. of processes.
{
int pCount = 5; // process count
gettimeofday (&first, &tzp); //start time
pid_t* pID = new pid_t[pCount];
for(int indexOfProcess=0; indexOfProcess<pCount; indexOfProcess++)
{
pID[indexOfProcess]= fork();
if (pID[indexOfProcess] == 0) // child
{
// code only executed by child process
// magic here
// The End
exit(0);
}
else if (pID[indexOfProcess] < 0) // failed to fork
{
cerr << "Failed to fork" << endl;
exit(1);
}
else // parent
{
// if(indexOfProcess==pCount-1) and a loop with waitpid??
gettimeofday (&second, &tzp); //stop time
if (first.tv_usec > second.tv_usec)
{
second.tv_usec += 1000000;
second.tv_sec--;
}
lapsed.tv_usec = second.tv_usec - first.tv_usec;
lapsed.tv_sec = second.tv_sec - first.tv_sec;
cout << "Job performed in " <<lapsed.tv_sec << " sec and " << lapsed.tv_usec << " usec"<< endl << endl;
}
}//for
}//main
Soluzione
Sposterei tutto dopo la riga " else // parent " in basso, al di fuori del ciclo for. Dopo il ciclo di forchette, fai un altro per ciclo con waitpid, quindi ferma l'orologio e fai il resto:
for (int i = 0; i < pidCount; ++i) {
int status;
while (-1 == waitpid(pids[i], &status, 0));
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
cerr << "Process " << i << " (pid " << pids[i] << ") failed" << endl;
exit(1);
}
}
gettimeofday (&second, &tzp); //stop time
Ho ipotizzato che se il processo figlio non termina normalmente con uno stato 0, allora non ha completato il suo lavoro e quindi il test non ha prodotto dati di temporizzazione validi. Ovviamente se i processi figlio sono supposti essere uccisi dai segnali o escono stati di ritorno diversi da 0, allora dovrai cambiare il controllo degli errori di conseguenza.
Un'alternativa che utilizza wait:
while (true) {
int status;
pid_t done = wait(&status);
if (done == -1) {
if (errno == ECHILD) break; // no more child processes
} else {
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
cerr << "pid " << done << " failed" << endl;
exit(1);
}
}
}
Questo non ti dice quale processo in sequenza non è riuscito, ma se ti interessa puoi aggiungere il codice per cercarlo nell'array pids e recuperare l'indice.
Altri suggerimenti
Il metodo più semplice è fare
while(wait() > 0) { /* no-op */ ; }
Questo non funzionerà se wait ()
fallisce per qualche motivo diverso dal fatto che non sono rimasti bambini. Quindi, con qualche controllo degli errori, questo diventa
int status;
[...]
do {
status = wait();
if(status == -1 && errno != ECHILD) {
perror("Error during wait()");
abort();
}
} while (status > 0);
Vedi anche la pagina di manuale wait (2)
.
Chiama wait (o waitpid) in un ciclo fino a quando tutti i bambini non vengono contabilizzati.
In questo caso, tutti i processi si stanno comunque sincronizzando, ma in generale si preferisce attendere quando è possibile svolgere più lavoro (ad es. pool di processi di lavoro), poiché tornerà quando cambia il primo stato del processo disponibile.
Credo che wait system call realizzerà ciò che stai cercando.
for (int i = 0; i < pidCount; i++) {
while (waitpid(pids[i], NULL, 0) > 0);
}
Non aspetterà nel giusto ordine, ma si fermerà poco dopo la morte dell'ultimo figlio.