2010-02-16 12 views
8

Próbuję napisać skrypt, który tworzy liczbę rozwidlonych procesów potomnych za pomocą pcntl_* functions.Rozwidlenia PHP i wiele sygnałów potomnych

Zasadniczo istnieje pojedynczy skrypt, który działa w pętli przez około minutę, okresowo odpytywanie bazy danych, aby sprawdzić, czy istnieje zadanie do uruchomienia. Jeśli takowy istnieje, powinien rozwidlić i uruchomić zadanie w oddzielnym procesie, aby rodzic nie był zatrzymany przez długo działające zadanie.

Ponieważ prawdopodobnie istnieje duża liczba zadań gotowych do uruchomienia, chcę ograniczyć liczbę tworzonych procesów podrzędnych. Dlatego śledzę liczbę procesów poprzez inkrementowanie zmiennej za każdym razem, gdy jest tworzony (a następnie wstrzymywanie, jeśli jest za dużo), a następnie zmniejszanie jej w procedurze obsługi sygnału. Niby tak:

define(ticks = 1); 

$openProcesses = 0; // how many we have open 
$max = 3;   // the most we want open at a time 

pcntl_signal(SIGCHLD, "childFinished"); 

while (!time_is_up()) { 
    if (there_is_something_to_do()) { 
     $pid = pcntl_fork(); 
     if (!$pid) {  // I am the child 
      foo();  // run the long-running task 
      exit(0);  // and exit 
     } else {   // I am the parent 
      ++$openProcesses; 
      if ($openProcesses >= $max) { 
       pcntl_wait($status); // wait for any child to exit 
      }       // before continuing 
     } 
    } else { 
     sleep(3); 
    } 
} 

function childFinished($signo) { 
    global $openProcesses; 
    --$openProcesses; 
} 

Działa to dość dużo ok większość czasu, z wyjątkiem, gdy dwa lub więcej procesów zakończyć jednocześnie - funkcja obsługi sygnału jest wywoływana tylko raz, co wyrzuca mój licznik. Powodem tego jest wyjaśnione przez „anonimowy” w notes of the PHP manual:

Wiele dzieci powrócić mniej niż liczba dzieci opuszczających w danej chwili jest sygnał SIGCHLD normalne zachowanie dla Unix (POSIX) systemów. SIGCHLD można odczytać jako "jeden lub więcej dzieci zmieniono status - idź zbadać swoje dzieci i zebrać ich wartości statusu".

Moje pytanie brzmi: Jak mogę sprawdzić dzieci i zebrać ich status? Czy istnieje jakiś niezawodny sposób sprawdzenia, ile procesów podrzędnych jest otwartych w danym momencie?

Używanie PHP 5.2.9

+0

prawdopodobnie po prostu za pomocą https://www.rabbitmq.com/ sprawiłoby, że cała rzecz była znacznie mniej podatna na błędy –

Odpowiedz

0

Można mieć dzieci wysłać SIGUSR1 do rodzica, gdy zaczynają, a potem SIGUSR2 przed wysuwanych. Inną rzeczą, z którą masz do czynienia podczas używania sygnałów pierwotnych, jest łączenie ich przez jądro, czego nie robi z sygnałami RT. Teoretycznie można połączyć dowolny Dowolny sygnał inny niż rt.

Możesz zaimplementować jakieś proste blokowanie za pomocą sqlite, gdzie tylko jedno dziecko na raz może mieć gadający gadżet. Tylko upewnij się, że dzieci obsługują normalnie śmiertelne sygnały, aby pozostały przy życiu, aby uwolnić blokadę.

2

Jednym ze sposobów jest zachowanie tablicy PID procesów potomnych, a w procedurze obsługi sygnału sprawdzenie każdego PID, aby sprawdzić, czy nadal działa. Kod (nietestowany) będzie wyglądał następująco:

define(ticks = 1); 

$openProcesses = 0; 
$procs = array(); 
$max = 3; 

pcntl_signal(SIGCHLD, "childFinished"); 

while (!time_is_up()) { 
    if (there_is_something_to_do()) { 
     $pid = pcntl_fork(); 
     if (!$pid) {  
      foo();   
      exit(0);  
     } else {   

      $procs[] = $pid; // add the PID to the list 

      ++$openProcesses; 
      if ($openProcesses >= $max) { 
       pcntl_wait($status);  
      }       
     } 
    } else { 
     sleep(3); 
    } 
} 

function childFinished($signo) { 

    global $openProcesses, $procs; 

    // Check each process to see if it's still running 
    // If not, remove it and decrement the count 
    foreach ($procs as $key => $pid) if (posix_getpgid($pid) === false) { 
     unset($procs[$key]); 
     $openProcesses--; 
    } 

}