Fork de Procesos en PHP

Feb 12, 2010 No Comments by

¿Necesitamos ejecutar más de una acción al mismo tiempo en PHP?
Como en otros lenguajes, PHP dispone de un par de herramientas para crear y manejar procesos duros, especificamente, la extensión Process Control (PCNTL) solo disponible en sistemas like Unix.

Cuando un proceso realiza una llamada a fork, el sistema crea otro proceso nuevo, exactamente igual al padre (en el punto de la bifurcación fork) pero con un PID distinto.

Dichos procesos son independiente y no tienen una comunicación directa, si deseamos comunicarlos entre si, debemos implementar algún tipo de comunicación entre procesos, como el envío de señales, comunicación por sockets unix o tcp, memoría compartida, etc.

Hay muchas tareas que obtienen un mayor rendimiento al realizarlas con procesos concurrente versus a un proceso, como la descarga de archivos, la busqueda de archivos, ping a un rango de red, etc.

Para llamar a la función fork del sistema desde PHP, tenemos que utilizar la función pcntl_fork, ella devuelve el pid del nuevo proceso al padre, por lo tanto, con un if podemos determinar ¿quién es el padre o el hijo?

<?php
$father_pid = getmypid();
$pid = pcntl_fork();
if ($pid == -1)
{
        die('could not fork');
}
else if ($pid)
{
        print "I'm the Father mi PID is ".$father_pid." and my children is ".$pid."\n";
        pcntl_wait($status); //Protect against Zombie children
}
else
{
        print "I'm the Children, my PID is ".getmypid()." and the PID of my Father is ".$father_pid."\n";
}

Podemos observar que tanto el padre como el hijo tiene PID distintos.

Descarga de Imágenes de un Documento Issuu

El siguiente script, es un pequeño ejemplo de la descarga de las imágenes de un documento issuu, utilizando uno o más procesos.

<?
/**
 * dIssu
 *
 * This script allows you to download the images from the books of issuu (fork version)
 *
 * @package pserver
 * @subpackage pserver.class
 * @author Pedro Vargas (info@deerme.org) http://deerme.org
 * @version 0.1
 */

 function issuu_download( $id , $pages )
 {
    $url = "http://image.issuu.com/%s/jpg/page_%s.jpg";
    if( is_numeric( $pages ) )
    {
        $t = $pages;
        $pages = array();
        $pages[] = $t;
    }

    foreach( $pages as $k => $page )
    {
        $c=0;
        $name = sprintf("%03s",$page);
        while( !copy(  sprintf( $url , $id , $page )  , "./". $name  .".jpg"  ) AND $c < 10  )
        {
            $c++;
        }
        echo "nr$page";
    }

 }

if ( !$argv[1]  )
    $id = "100112181114-863a0894edd54624b859f4d370537f75";
else
    $id = $argv[1];

if ( !$argv[2]  )
    $ini = 1;
else
    $ini = (int)$argv[2];

if ( !$argv[3]  )
        $fin = 10;
else
        $fin = (int)$argv[3];

if ( !$argv[4] )
{
    print "nProcess Fork Versionnr";
    $fv = 1;
}
else
{
    print "nMono Process Versionnr";
    $fv = 0;
}

$maxchildren = 4;       

for( $i=$ini;$i<=$fin;$i++ )
{

    $npages[] = $i;
    if ( (count($npages) % (($fin-$ini)/$maxchildren ) == 0) OR $i == $fin )
    {
        // Fork Version
        if ( $fv )
        {
            $pid = pcntl_fork();
            if($pid == -1)
            {
                die('could not fork');
            }
            else if ($pid)
            {
                // I am the Father
                $pids[] = $pid;
                unset( $npages );
            }
            else
            {
                // I am the Son
                issuu_download( $id , $npages );
                die();
            }
        }
        // One process
        else
        {
            issuu_download( $id , $npages );
            unset( $npages );
        }

    }   

}

foreach($pids as $pid)
{
    pcntl_waitpid($pid, $status);
}

?>

php dissuu.php iddoc pini pfin [0/1]

Dispone de 4 parametros

1.- iddoc es el id de un documento issuu (lo podemos ver en el fuente de un documento issuu).
2.- pini, es la página de inicio a descargar (generalmente 1).
3.- pfin, es la página final a descargar.
4.- 0/1, este parametro es opcional, permite correr el script utilizando uno o más procesos concurrentes.

Comparación

Vamos a descargar dos documentos, cada uno utilizando un solo proceso o varios procesos concurrentes, el tiempo de ejecución lo vamos a medir con el comando time.

copiapo:/tmp# time php downissuuF.php 100112181114-863a0894edd54624b859f4d370537f75 1 16

Process Fork Version

real    0m26.533s
user    0m0.020s
sys     0m0.104s
copiapo:/tmp# time php downissuuF.php 100112181114-863a0894edd54624b859f4d370537f75 1 16 false

Mono Process Version

real    0m42.070s
user    0m0.000s
sys     0m0.064s
copiapo:/tmp# time php downissuuF.php 090829152404-eb2777e1d8614ec8918c0ee0e3b6a80a 1 66

Process Fork Version

real    1m20.116s
user    0m0.016s
sys     0m0.316s
copiapo:/tmp# time php downissuuF.php 090829152404-eb2777e1d8614ec8918c0ee0e3b6a80a 1 66 false

Mono Process Version

real    1m58.648s
user    0m0.008s
sys     0m0.092s
copiapo:/tmp#

Podemos notar (en los dos ejemplos) que al utilizar más procesos, la tarea total, fue más rapida que al ejecutar un solo proceso. Ambas ejecuciones fueron en condiciones muy similares (el mismo servidor, el mismo ancho de banda, etc.) y realizaron la misma tarea (descargar varios archivos), finalmente la diferencia de tiempo, en gran medida, depende de todas las pausas que realiza el sistema operativo en cada proceso, ya que accedemos a un recurso de red (I/O), el bloqueo entre procesos, el tiempo que de ejecución real en la CPU de cada proceso, etc.

PHP

About the author

Ingeniero en Informática, Oracle Certified Master Java EE 6 Enterprise Architect, Oracle Certified Professional Java Programmer. Experto en distintas ramas de la computación y otras "yerbas" xD. Si te gusto este post, sígueme en @deerme_org, escríbeme a info AT deerme.org o contactame por linkedin.
No Responses to “Fork de Procesos en PHP”

Leave a Reply


* six = 36