Poesía Binaria

Variables compartidas entre procesos hijos en C [fork()]

Otra forma de enfrentarse con la concurrencia, en el maravilloso mundo de hacer varias cosas al mismo tiempo, es utilizar procesos hijos con fork(). La principal diferencia con respecto a los threads es que éstos son procesos completos, es decir, el sistema operativo les ha dado una zona de memoria de código, otra de datos, y otra de pila, a diferencia de los threads que compartían código y datos, sólo tenían la pila diferente.

Pero, ¿qué pasa si queremos compartir información entre nuestros procesos hijo? En principio, si lo intentamos hacer como con los threads, no va a funcionar, precisamente porque al ser procesos diferentes, utilizarán zonas diferentes de memoria para almacenar sus datos. Por ejemplo, si hacemos:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sys/wait.h>

int main()
{
  int child;
  int *number = (int*)malloc(sizeof(int));
  int i;

  *number = 10;

  child = fork();

  if (child==-1)
    {
      exit(1);
    }
  else if (child==0)
    {
      for (i=0; i<10; ++i)
    {
      usleep(100);
      printf ("CHILD -- Number: %d\n", *number);
      *number=i;
    }
      exit(0);
    }
  else
    {
      for (i=20; i<30; ++i)
    {
      usleep(100);
      printf ("MAIN -- Number: %d\n", *number);
      *number=i;
    }
    }
  wait(NULL);
  free(number);
}

Lo ideal sería que el proceso pricipal (MAIN) fuera capaz de ver el valor que ha dejado el hijo (CHILD) y viceversa, pero no, son valores totalmente independientes… y mira que hemos reservado memoria aparte con malloc. Es que un fork() copiará los valores de todas las variables en el momento del fork, pero el espacio en memoria es diferente.

Para ello tenemos que hacer uso de la memoria compartida, para ello, en lugar de malloc, vamos a utilizar:

1
2
  int *number = (int*)mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE,
               MAP_SHARED | MAP_ANONYMOUS, -1, 0);

Para utilizar mmap() debemos incluir la biblioteca <sys/mman.h>

Al igual que con malloc() utilizamos free() para liberar la memoria, con mmap() utilizaremos:

1
munmap(number, sizeof(int));

Aunque del mismo modo que utilizando hilos podíamos tener problemas de condición de carrera, en este caso estamos en las mismas por lo que se aconseja el uso de mutex.
De esta forma podremos utilizar una zona común para almacenar esta información, y ahora sí que el CHILD verá el valor del MAIN y viceversa.
Foto: Rudolf Vicek (Flickr) CC-by

También podría interesarte....