En esta entrada vamos a implementar y explicar uno de los mecanismos de comunicación entre procesos en Windows. Utilizaremos IPC («Inter Process Communication») mediante memoria compartida.
Nuestro objetivo es comunicar un proceso A con un proceso B y enviar cualquier tipo de información de un proceso al otro.
Cada Sistema Operativo tiene diferentes mecanismos de comunicación entre procesos, en Windows hay bastantes mecanismos disponibles, y se dividen en 2 categorías prinpalmente:
- Comunicación entre procesos en una misma máquina. Es decir, procesos que no se comunican mediante red.
- Comunicación entre procesos que están en diferentes redes/máquinas. Esto conlleva realizar la comunicación mediante la red y por lo tanto, utilizar sockets.
En esta entrada veremos como realizar la comunicación de 2 procesos en una misma máquina utilizando para ello el API de Windows.
En el siguiente enlace tenéis la documentación oficial de Microsoft de los diferentes mecanismos de comunicación disponibles.
Y en estos 2 enlaces la documentación sobre la que hemos basado nuestro post:
Memoria compartida mediante un nombre en Windows
¿ En qué consiste comunicar procesos mediante memoria compartida ?
Básicamente ambos procesos abren un descriptor hacia una zona de memoria compartida por ambos y pueden tanto escribir como leer. En nuestro post el primer proceso escribirá, el segundo proceso leerá los datos escritos por el primer proceso y enviará un mensaje diciendo que ha recibido los datos.
Nuestro proceso escritor
Veamos el código y posteriormente expliquémoslo:
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
struct Persona {
char* nombre, *apellidos;
int edad;
double ordenadores;
};
#define BUF_SIZE 1024
TCHAR szName[] = TEXT("Global\MyFileMapping");
int _tmain()
{
HANDLE hMapFile;
void* pBuf;
hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE, // use paging file
NULL, // default security
PAGE_READWRITE, // read/write access
0, // maximum object size (high-order DWORD)
BUF_SIZE, // maximum object size (low-order DWORD)
szName); // name of mapping object
if (hMapFile == NULL)
{
_tprintf(TEXT("Could not create file mapping object (%d).\n"),
GetLastError());
return 1;
}
printf("Area de memoria creada para compartir!\n");
pBuf = (void*)MapViewOfFile(hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if (pBuf == NULL)
{
_tprintf(TEXT("Could not map view of file (%d).\n"),
GetLastError());
CloseHandle(hMapFile);
return 1;
}
printf("Puntero hacia la memoria compartida obtenido para escribir en ella\n");
struct Persona* persona1 = (struct Persona*)malloc(sizeof(struct Persona));
persona1->nombre = (char*)malloc(30);
persona1->apellidos = (char*)malloc(50);
persona1->edad = 32;
persona1->ordenadores = 4;
strncpy_s(persona1->nombre,30, "Ivan", strlen("Ivan") + 1);
strncpy_s(persona1->apellidos, 50, "Ramon Moran", strlen("Ramon Moran") + 1);
CopyMemory((void*)pBuf, persona1->nombre, 30);
CopyMemory((char*)pBuf + 30, persona1->apellidos, 50);
CopyMemory((int*)pBuf + 80, &persona1->edad, sizeof(int));
CopyMemory((double*)pBuf + 84, &persona1->ordenadores, sizeof(double));
printf("Datos copiados en la memoria. Pulsa 2 veces intro para leer el mensaje de vuelta y salir del programa\n");
_getch();
printf("%s\n", (char*)pBuf);
free(persona1->nombre);
free(persona1->apellidos);
free(persona1);
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
return 0;
}
El struct que vamos a enviar es:
struct Persona {
char* nombre, *apellidos;
int edad;
double ordenadores;
};
Definimos el tamaño de la zona de memoria compartida y el nombre de la misma, el nombre se utilizará para que el segundo proceso la pueda abrir:
#define BUF_SIZE 1024
TCHAR szName[] = TEXT("Global\MyFileMapping");
Es decir, el primer proceso registrar la zona de memoria y le asigna un nombre y el segundo proceso abrirá la zona de memoria registrada por el primer proceso.
Creación de la zona de memoria:
HANDLE hMapFile;
void* pBuf;
hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE, // use paging file
NULL, // default security
PAGE_READWRITE, // read/write access
0, // maximum object size (high-order DWORD)
BUF_SIZE, // maximum object size (low-order DWORD)
szName); // name of mapping object
if (hMapFile == NULL)
{
_tprintf(TEXT("Could not create file mapping object (%d).\n"),
GetLastError());
return 1;
}
printf("Area de memoria creada para compartir!\n");
Mediante la línea 5 especificamos que utilizaremos memoria y no un archivo, ya que también se puede compartir información mediante procesos utilizando ficheros, pero esto es menos eficiente que hacerlo mediante memoria.
Mediante la línea 7 especificamos que será una zona de memoria que servirá tanto para leer como para escribir.
Mediante la línea 9 especificamos el tamaño de la zona de memoria y mediante la línea 10 cuál será su nombre.
Una vez creada la zona de memoria tenemos que obtener un puntero hacía la misma:
pBuf = (void*)MapViewOfFile(hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if (pBuf == NULL)
{
_tprintf(TEXT("Could not map view of file (%d).\n"),
GetLastError());
CloseHandle(hMapFile);
return 1;
}
printf("Puntero hacia la memoria compartida obtenido para escribir en ella\n");
Ahora ya podemos crear nuestro struct Persona y escribirlo en la zona de memoria compartida:
struct Persona* persona1 = (struct Persona*)malloc(sizeof(struct Persona));
persona1->nombre = (char*)malloc(30);
persona1->apellidos = (char*)malloc(50);
persona1->edad = 32;
persona1->ordenadores = 4;
strncpy_s(persona1->nombre,30, "Ivan", strlen("Ivan") + 1);
strncpy_s(persona1->apellidos, 50, "Ramon Moran", strlen("Ramon Moran") + 1);
CopyMemory((void*)pBuf, persona1->nombre, 30);
CopyMemory((char*)pBuf + 30, persona1->apellidos, 50);
CopyMemory((int*)pBuf + 80, &persona1->edad, sizeof(int));
CopyMemory((double*)pBuf + 84, &persona1->ordenadores, sizeof(double));
printf("Datos copiados en la memoria. Pulsa 2 veces intro para leer el mensaje de vuelta y salir del programa\n");
_getch();
Aquí simplemente copiamos nuestros datos en la zona de memoria compartida. Fijaos que utilizamos aritmética de punteros en cada «CopyMemory».
Por último, leemos lo que nos devuelve el proceso 2 y liberamos la memoria:
printf("%s\n", (char*)pBuf);
free(persona1->nombre);
free(persona1->apellidos);
free(persona1);
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
Nuestro proceso lector
Veamos el código:
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#pragma comment(lib, "user32.lib")
#define BUF_SIZE 1024
TCHAR szName[] = TEXT("Global\MyFileMapping");
int _tmain()
{
HANDLE hMapFile;
void* pBuf;
hMapFile = OpenFileMapping(
FILE_MAP_ALL_ACCESS, // read/write access
FALSE, // do not inherit the name
szName); // name of mapping object
if (hMapFile == NULL)
{
_tprintf(TEXT("Could not open file mapping object (%d).\n"),
GetLastError());
return 1;
}
printf("Zona de memoria compartida abierta!\n");
pBuf = (void*)MapViewOfFile(hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if (pBuf == NULL)
{
_tprintf(TEXT("Could not map view of file (%d).\n"),
GetLastError());
CloseHandle(hMapFile);
return 1;
}
printf("Puntero hacia la zona de memoria compartida abierto\n");
printf("%s\n", (char*)pBuf);
printf("%s\n", (char*)pBuf + 30);
printf("%d\n", *((int*)pBuf + 80));
printf("%lf\n", *((double*)pBuf + 84));
CopyMemory((char*)pBuf, "Recibido", strlen("Recibido") + 1);
//MessageBox(NULL, (LPCWSTR)pBuf, TEXT("Process2"), MB_OK);
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
return 0;
}
Primero abrimos la zona de memoria compartida:
HANDLE hMapFile;
void* pBuf;
hMapFile = OpenFileMapping(
FILE_MAP_ALL_ACCESS, // read/write access
FALSE, // do not inherit the name
szName); // name of mapping object
if (hMapFile == NULL)
{
_tprintf(TEXT("Could not open file mapping object (%d).\n"),
GetLastError());
return 1;
}
printf("Zona de memoria compartida abierta!\n");
La abrimos con permisos de lectura y escritura y con el nombre mediante el cuál la ha creado nuestro proceso escritor.
Posterior a esto obtenemos el puntero hacia la zona de memoria compartida:
pBuf = (void*)MapViewOfFile(hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if (pBuf == NULL)
{
_tprintf(TEXT("Could not map view of file (%d).\n"),
GetLastError());
CloseHandle(hMapFile);
return 1;
}
printf("Puntero hacia la zona de memoria compartida abierto\n");
Leemos los datos y los mostramos:
printf("%s\n", (char*)pBuf);
printf("%s\n", (char*)pBuf + 30);
printf("%d\n", *((int*)pBuf + 80));
printf("%lf\n", *((double*)pBuf + 84));
Por último, enviamos un mensaje al proceso A y liberamos los recursos:
CopyMemory((char*)pBuf, "Recibido", strlen("Recibido") + 1);
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
La ejecución de ambos procesos
Lanzamos el primer proceso:
Lanzamos el segundo proceso:
Obtenemos el mensaje de vuelta en el primer proceso: