2010-04-14 14 views
14

Come risolvere set_time_limit non influisce su PHP-CLI?set_time_limit non influisce su PHP-CLI

#!/usr/bin/php -q 
<?php 
set_time_limit(2); 
sleep(5); // actually, exec() call that takes > 2 seconds 
echo "it didn't work again"; 
+0

Come Pascal MARTIN sottolinea, set_time_limit non è l'approccio corretto. In qualche modo ho bisogno di uccidere questo script dopo alcuni secondi. – Devrim

+1

stesso problema, ha aggiunto taglie, ha bisogno di un modo per fermare il processo cli del processo generato da cron dopo 5 minuti – Moak

+0

Giusto per chiarire che la taglia non è destinata a Pascal MARTIN, poiché la sua risposta non funziona per php-cli – Moak

risposta

9

La soluzione qui è utilizzare pcntl_fork(). Temo che sia solo POSIX. PHP dovrà essere compilato con --enable-pcntl e non con --disable-posix. Il codice dovrebbe apparire qualcosa di simile:

<?php 

function done($signo) 
{ 
    // You can do any on-success clean-up here. 
    die('Success!'); 
} 

$pid = pcntl_fork(); 
if (-1 == $pid) { 
    die('Failed! Unable to fork.'); 
} elseif ($pid > 0) { 
    pcntl_signal(SIGALRM, 'done'); 
    sleep($timeout); 
    posix_kill($pid, SIGKILL); 
    // You can do any on-failure clean-up here. 
    die('Failed! Process timed out and was killed.'); 
} else { 
    // You can perform whatever time-limited operation you want here. 
    exec($cmd); 
    posix_kill(posix_getppid(), SIGALRM); 
} 

?> 

Fondamentalmente Quello che fa è forcella un nuovo processo che gestisce il blocco else { ... }. Alla conclusione (con esito positivo) inviamo un allarme al processo genitore, che sta eseguendo il blocco elseif ($pid > 0) { ... }. Quel blocco ha un gestore di segnale registrato per il segnale di allarme (il callback done()) che termina correttamente. Fino a quando non viene ricevuto, il processo genitore dorme. Al termine del timeout sleep(), invia un SIGKILL al processo figlio (presumibilmente appeso).

+0

+1, una risposta molto ragionevole – Brad

15

Il limite max_execution_time, che è ciò set_time_limit set, conta (almeno, su Linux) il tempo che è trascorso dal processo di PHP, durante il lavoro.

Citando la pagina del manuale di set_time_limit():

Nota: La funzioneset_time_limit() e la configurazione direttiva max_execution_timesolo influenzano il tempo di esecuzione dello script sé.
Il tempo utilizzato per attività esterno del esecuzione dello script come sistema telefoniche usando system(), streaming operazioni, query di database, ecc è non inclusa nel determinare la tempo massimo che lo script è stato corsa .
Questo non è vero su Windows in cui il tempo misurato è reale.

Quando si utilizza sleep(), lo script PHP non funziona: è solo in attesa ... Così i 5 secondi si è in attesa non vengono prese in considerazione dal limite max_execution_time.

+0

grazie per la risposta, tuttavia ho ancora risolto il problema, c'è comunque che posso farlo usando un altro approccio? – Devrim

+1

Beh, direi che non dovremmo non 'dormire()', se non vuoi che la sceneggiatura rimanga viva per più tempo di quanto sia necessario ;-) ;;; più seriamente, 'max_execution_time' è inteso come precauzione di sicurezza contro script che richiederebbero troppe risorse e non come un qualsiasi tipo di meccanismo di temporizzazione preciso. –

+0

c'è un exec() in esecuzione invece di sleep() lì. e alcune cose di exec() si bloccano indefinitamente. Sto anche cercando di ucciderlo da exec (su linux shell), come exec (kill_after15secs myscript.sh), ma non riesco a trovare che sia ... – Devrim

4

Molto hacker, ma dal momento che utilizzo molto script di cli, ammetto lo vergognosamente di aver schiaffeggiato un simile hack in passato. 2 script necessari.

Il tuo primo script cron (quello che si blocca) registra il suo orario di inizio e processid in un file flat -> la funzione getmypid sarà tuo amico per questo. Un secondo script eseguito da cron ogni (x) minuti controlla il flatfile, uccide tutto ciò che ha passato il tempo di esecuzione assegnato, cancella il flatfile e anche registra in un altro file, se lo si desidera.

Buona fortuna;)

UPDATE:

Ricordato questa domanda ed è tornato a postare questo. Mentre rovistavo nell'account github di Sebastian Bergmann mi sono imbattuto in php-invoker. Nelle parole dell'autore:

classe di utilità per invocare callable PHP con un timeout.

Anche se la risposta corrente è molto buono per l'uso Linux basata non portatile, se è necessaria una soluzione cross-platform questa soluzione dovrebbe essere Windows compatibili, per quelle povere anime in esecuzione su windoze. Essendo il Sig. Bergmann un dio PHP, sono assolutamente in grado di garantire la qualità della sceneggiatura.

+0

avreste bisogno di una sorta di cron job manager per gestire i processi di avvio/registrazione/arresto automaticamente, ma funzionerebbe perfettamente – Quamis

5

Potresti provare a eseguire il tuo php tramite exec e utilizzare il comando "timeout"? (Http: //packages.ubuntu.com/lucid/timeout)

utilizzo: timeout [-signal] comando orario.

+1

Super semplice, funziona esattamente come previsto, per esempio ' timeout 5m/usr/bin/php/var/www/example.com/cron.php' – Moak

4

Sto dicendo che la sceneggiatura è sospesa in un ciclo in qualche modo. Perché non fare semplicemente uno $STARTUP_TIME=time() all'avvio dello script e quindi continuare a controllare il ciclo se lo script esce per uscire?

+0

+1 è solo un modo normale Non capisco perché non è stato utilizzato e non è stato suggerito in anticipo –

+0

@ OZ: se lo script * exec * utes un processo verrà bloccato - attendi che il processo figlio termini - prima di passare alla successiva istruzione nello script. Se il processo eseguito restituisce il controllo allo script immediatamente (ad es. usando l'operatore '&' di bash, questa logica funzionerà –

2

EDIT 1

ho notato questo commento nel manuale di PHP:

Si prega di notare che, sotto Linux, tempo dormendo viene ignorato, ma sotto di Windows, conta come tempo di esecuzione.

Per quanto ho capito, il sonno è implementato come una chiamata di sistema e quindi ignorato da PHP as described in the manual.

EDIT 2

c'è un exec() in esecuzione invece di sleep() lì. e alcune cose exec() si bloccano indefinitamente. Sono anche ricerca per l'uccisione dall'interno exec (sulla shell di Linux), come exec (kill_after15secs myscript.sh), ma io non riesco a trovare che o

vedo ora l'attuale domanda. Supponendo che si sta lavorando in un ambiente Lunix/Unix, è possibile trovare una soluzione intorno a queste linee:

<?php $pid = system('sh test.sh >test-out.txt 2>&1 & echo $!'); ?> 

Indovinate un po ', questo ha catturato l'ID di processo del processo è stato avviato. Puoi registrarlo in un database o file di testo insieme al timestamp. In un altro script cron che viene eseguito, ad esempio, ogni 5 minuti, recuperare tutti gli ID di processo che sono stati creati 5 minuti fa e verificare se sono ancora in esecuzione:

<?php $status = system('ps ' . $pid); ?> 

Se il processo è ancora in esecuzione, si ottengono due righe di output :

PID TTY STAT TIME COMMAND 
2044 ? S 0:29 sh test.sh 

Se è così, terminare in questo modo:

<?php system('kill ' . $pid); ?> 

ho scritto un articolo su creating background processes in PHP (e loro la caccia in seguito). Tutti gli esempi sono stati copiati da lì.

EDIT 3

tutti i punti collegati tra loro:

<?php 
    $pid = system('sh test.sh >test-out.txt 2>&1 & echo $!'); 
    $timer = 300; 
    while(--$timer) { 
     sleep(1); 
     $status = system('ps ' . $pid); 
     $status = explode("\n", $status, 2); // I am not sure, please experiment a bit here 
     if (count($status) == 1 || trim($status[ 1 ]) == '') { 
      die('spawned process ended successfully'); 
     } 
    } 
    system('kill ' . $pid); 
    die('spawned process was terminated'); 
?> 
Problemi correlati