Concurrencia de procesos con PHP, el problema de los dados implementado con tuberías (pipes)

Hoy hacemos un ejercicio típico que suelen poner en algún momento a los estudiantes del Institut Obert de Catalunya (IOC), la Universitat Politècnica de Catalunya (UPC) o la Universitat Oberta de Catalunya (UOC), entre otros, para iniciarse en la programación de procesos concurrentes.

El ejercicio que presentamos hoy tiene por objetivo familiarizarse con los aspectos más próximos a la máquina desde el sistema operativo subyacente. También sirve para mostrar que con PHP se pueden programar sistemas; por ejemplo, con PHP podemos hacer programas de administración que, además, pueden controlarse desde una cómoda interfaz gráfica (GUI) web.

El problema es un viejo conocido de la gestión de procesos: ¡el famoso juego de dados donde hay un proceso padre que se replica y recoge el resultado que calcula su hijo dado! ¿Lo conoces?

Hay varias versiones de este ejercicio con diferentes grados de complicación. La variante más sencilla dice que hay que hacer un proceso que se copie a sí mismo, luego, el hijo de dicho proceso tira un dado (calcula un número aleatorio entre 1 y 6); finalmente, el padre recoge el resultado que le pasa su hijo e imprime el resultado en la pantalla.

Voy a suponer que ya estás familiarizado/a con la gestión de los procesos en sistemas de tipo UNIX. Este tema es bastante denso en teoría, de modo que no me extenderé mucho explicando los fundamentos. Al comienzo puede parecer un poco apabullante, pero luego, cuando uno se familiariza con los conceptos básicos, no tiene mucha complicación.

Por ejemplo, debes saber que hay varias maneras de comunicar procesos. La que utilizo en mi solución se basa en las pipes, que no son más que ficheros temporales utilizados para enviar y recibir información.

¿Tienes que hacer este ejercicio o algún programa parecido basado en pipes o tuberías? ¿Cómo vas de tiempo? Don’t worry… Si andas apurado, programarivm.com te ayuda, éste es un buen sitio para hacer copy&pastes. Allá va pues mi solución al problema de los dados basada en las tuberías (o pipes) como mecanismo de comunicación entre procesos.

El programa está pensado para ejecutarse desde la línea de comandos de PHP, PHP CLI, así que no olvides poner tu shebang correspondiente.

$pipe = '/tmp/pipe';
$mode = 0600;

if (!file_exists($pipe)) {
    posix_mkfifo($pipe, $mode);
}

$pid = pcntl_fork();

switch ($pid) {
    case -1:
        die("Error al utilizar la función fork\n");
        break;

    case 0:
        $resultRoll = exec('/home/jordi/./dado.php');
        $f = fopen($pipe, 'w');
        fwrite($f, $resultRoll);
        exit;

    default:
        $f = fopen($pipe, 'r');
        $resultRoll = fgets($f);
        printf("La tirada de mi hijo con PID $pid es $resultRoll");
        pcntl_wait($status);
        unlink($pipe);
        exit;
}

Pero ojo. Las funciones PHP que gestionan el ciclo de vida de un proceso POSIX, tales como pcntl_fork(), es decir, todas las que necesitamos para resolver este ejercicio, pertenecen al grupo de funciones PHP PCNTL. Sin embargo, por cuestiones de seguridad, este grupo de funciones no está disponible de forma predeterminada en una instalación PHP típica. Para habilitar estas funciones tienes que seguir las instrucciones del manual PHP.