El sistema operativo Windows provee mecanismos para facilitar la comunicación y el intercambio de datos entre aplicaciones. Conjuntamente, las actividades habilitadas por estos mecanismos se llaman “comunicación entre procesos” (IPC, interprocess comunications). Algunos tipos de IPC facilitan la división de trabajo entre varios procesos especializados. Otros tipos de IPC facilitan la división de trabajo entre computadoras en una red.
Típicamente, las aplicaciones pueden usar IPC categorizados como clientes o servidores. Un cliente es una aplicación o un proceso que solicita un servicio de alguna otra aplicación o proceso. Un servidor es una aplicación o un proceso que responde a los requerimientos de un cliente. Muchas aplicaciones actúan tanto como cliente como de servidor.
Luego de decidir si una aplicación se beneficiaría de IPC, se debe decidir cuál de los métodos de IPC disponibles usar. Lo más probable es que una aplicación usará varios mecanismos de IPC.
Las respuestas a las siguientes preguntas determinarán si una aplicación puede beneficiarse usando uno o más mecanismos de IPC.
- ¿Debería la aplicación ser capaz de comunicarse con otras aplicaciones en ejecutándose en otra computadoras dentro de una red, o es suficiente para la aplicación sólo comunicarse con aplicaciones en la computadora local?
- ¿Debería la aplicación ser capaz de comunicarse con aplicaciones ejecutándose en otras computadoras que se pueden estar ejecutando bajo un sistema operativo diferente?
- ¿Debería el usuario tener que elegir cuáles son las otras aplicaciones con las que se comunica la aplicación en cuestión, o puede la misma implícitamente encontrar sus compañeros cooperantes?
- ¿Debería la aplicación comunicarse con muchas aplicaciones diferentes en un sentido general, tal como permitir operaciones de cortar-y-pegar con alguna otra aplicaciones, o deberían sus requerimientos de comunicación limitarse a un conjunto restringido de interacciones con otras aplicaciones específicas?
- ¿Es el desempeño un aspecto crítico de la aplicación? Todos los mecanismos de IPC incluyen mucho “overhead”.
- ¿Debería la aplicación ser una aplicación con GUI, o es una aplicación de consola? Algunos mecanismos de IPC requieren una aplicación con GUI.
| Mecanismos de IPC Soportados por Windows |
Memoria compartida y Mapeo de archivos (Shared Memory & File mapping)
Al igual que en la mayoría de los sistemas operativos modernos, Windows provee un mecanismo para compartir memoria entre los procesos y el sistema operativo. La memoria compartida puede definirse como la memoria que es visible por más de un proceso, o que está presente en más de un espacio de direcciones virtuales de procesos. Por ejemplo, si dos procesos usan la misma DLL, tendría sentido cargar las páginas de código referenciadas de aquella DLL a la memoria física sólo una vez y compartir esas páginas entre los procesos que mapean la DLL, como lo muestra la siguiente figura.

Cada proceso mantendría aún su área de memoria privada en la cual almacena información privada, pero las instrucciones de programa y las páginas de datos sin modificar podrían ser compartidas sin efectos adversos. Esta forma de compartir sucede automáticamente porque las páginas de código en imágenes ejecutables son mapeadas en modo de “solo-ejecución” y las páginas modificables son mapeadas en “copia-sobreescritura” (Copy-on-write).
Las primitivas subyacentes en el gestor de memoria que suele implementar la memoria compartida, son llamadas “Objetos sección” (section objects), los cuales son llamados “Objetos de mapeo de archivos” (file mapping objects) en la API de Windows.
Esta primitiva fundamental en el gestor de memoria es usada para mapear direcciones virtuales, ya sea en memoria principal, en la página de un archivo, o en algún otro archivo al cual una aplicación quiere acceder mientras esté en memoria. Una sección puede ser abierta por un proceso o por muchos. En otras palabras, los objetos secciones no necesariamente equivalen a memoria compartida.
Una sección objeto puede estar conectada a un archivo abierto en disco (llamado archivo mapeado) o a memoria comprometida (para proveer memoria compartida). Un archivo mapeado permite a un proceso tratar el contenido de un archivo como si fuera un bloque de memoria en el espacio de direcciones del proceso. El proceso puede usar operaciones de puntero simple para examinar y modificar dicho contenido. Cuando dos o más procesos acceden al mismo archivo mapeado, capa proceso recibe un puntero a memoria en su respectivo espacio de direcciones que puede usar para leer o modificar el contenido del archivo en cuestión. Tener en cuenta que los procesos deben usar objetos de sincronización, tales como semáforos, para prevenir la corrupción de datos en ambientes multitareas.
Las secciones mapeadas para comprometer memoria son llamadas “secciones respaldadas de archivos de páginas” (page file backed sections), porque las páginas son escritas a los archivos de paginación si la memoria así lo dispone (como Windows puede correr sin archivos de paginación, las secciones respaldadas de archivos de páginas pueden de hecho ser respaldadas solo por la memoria física). Al igual que con cualquier otra página que está hecha visible al modo usuario, páginas comprometidas compartidas son siempre rellenadas con ceros cuando son accedidas por primera vez.
Para crear un objeto sección, se invoca la función de Windows CreateFileMapping, especificando el manejador del archivo a mapear (o INVALID_HANDLE_VALUE para sección respaldada de archivo de páginas), y opcionalmente un nombre y un descriptor de seguridad. Si la sección tiene un nombre, otros procesos pueden abrirla con OpenFileMapping. O se puede conceder acceso al objeto sección a través de la herencia de manejadores (especificando que el manejador sea heredable cuando se abra o se cree el manejador) o duplicando manejadores (a través deDuplicateHandle).
Los controladores de dispositivos también pueden manipular objetos sección con las funciones ZwOpenSection, ZwMapViewOfSection, y ZwUnmapViewOfSection.
Un objeto sección puede referir a archivos mucho más grandes de lo que cabe en el espacio de direcciones de un proceso. Para acceder a un objeto sección muy grande, un proceso puede mapear solo la porción del objeto sección que el mismo requiere (llamada “vista” de la sección) invocando la función MapViewOfFile y luego especificando el rango a mapear. Mapear vistas permite a un proceso conservar su espacio de dirección porque sólo las vistas de los objetos sección que se necesitan al momento deben ser mapeadas a memoria.
Las aplicaciones de Windows pueden usar archivos mapeados para efectuar convenientemente I/O (entrada/salida) a archivos simplemente haciéndolos aparecer en sus espacios de direcciones. Las aplicaciones de usuario no son las únicas consumidoras de los objetos sección: el cargador de imágenes usa objetos sección para mapear imágenes ejecutables, DLLs, y controladores de dispositivos a la memoria, y el gestor de caché los usa para acceder a información en archivos cacheados.
Protección de la memoria
Para que los procesos de usuarios no puedan corromper otros espacios de direcciones de otros procesos o incluso del sistema operativo, Windows provee cuatro modos de protección primarios.
Primero (1), todas las estructuras de datos de todo el sistema y los grupos de memoria usados por los componentes del sistema en modo kernel, pueden ser accedidos solo mientras se esté en modo kernel; los hilos en modo usuario no pueden acceder a estas páginas. En caso que esto suceda, el hardware genera una falla, la cual el gestor de memoria se la reporta al hilo como una violación de acceso.
Segundo (2), cada proceso tiene un espacio de direcciones privado, separado y protegido de ser accedido por cualquier hilo que pertenezca a otro proceso. Las únicas excepciones son si el proceso decide compartir páginas con otros procesos o si otro proceso tiene acceso de lectura o escritura en memoria virtual para el objeto del proceso, y así puede usar las funciones ReadProcessMemory o WriteProcessMemory. Cada vez que un hilo referencia una dirección, el hardware de la memoria virtual, junto con el gestor de memoria, interviene y traduce la dirección virtual en una física. Controlando como se realiza esta traducción, Windows se asegura que los hilos en ejecución en un proceso no accedan inapropiadamente a una página que pertenezca a otro proceso.
Tercero (3), sumado a la protección implícita que la traducción de dirección virtual a física ofrece, todos los procesadores soportados por Windows proveen algunasformas de protección de memoria controladas por hardware (como Lectura/Escritura, solo lectura, etcétera), cuyos detalles exactos varían de acuerdo al procesador.
Y finalmente (4), los objetos sección de la memoria compartida tienen listas de control de acceso de Windows estándar (ACLs) que son chequeadas cuando un proceso requiere accederlas, y así limita el acceso a memoria compartida a aquellos procesos que posean los derechos apropiados. La seguridad también aparece cuando un hilo crea una sección que contiene un archivo mapeado. Para crear la sección, el hilo debe tener al menos acceso de lectura al objeto archivo subyacente o la operación fallará.
Una vez que un hilo ha abierto exitosamente un manejador de una sección, sus acciones aún están sujetas al gestor de memoria y a las protecciones de páginas basadas en hardware descriptas anteriormente. Un hilo puede cambiar el nivel de protección de una página en páginas virtuales en una sección si el cambio no viola los permisos en la ACL para dicho objeto sección. Por ejemplo, el gestor de memoria permite a un hilo cambiar las páginas de una sección de solo-lectura a tener el acceso copia-sobre-escritura pero no a que tenga el acceso de lectura/escritura. El acceso copia-sobre-escritura es permitido porque el mismo no tiene efecto sobre los otros procesos con los cuales se comparte la información.
Para concluir, el mapeo de archivos es muy eficiente y también provee atributos de seguridad soportados por el sistema operativo que pueden ayudar a prevenir corrupción de datos desautorizados. Para compartir datos con el mapeo de archivos, se deben proveer sincronización entre los procesos involucrados. Cabe aclarar, que dicho mecanismo solo es usado entre procesos de una máquina local, no puede ser usado sobre una red de trabajo.
Tuberías (Pipes)
Hay dos tipos de tuberías para la comunicación bidireccional: las tuberías anónimas, y las tuberías nombradas
Tuberías anónimas
Las tuberías anónimas habilitan a procesos relacionados transferir información entre ellos.Típicamente, una tubería anónima es usada para redirigir la entrada o salida estándar de un proceso hijo de manera que pueda intercambiar información con su proceso padre. Para intercambiar información en ambas direcciones (operación dúplex), se deben crear dos tuberías anónimas. El proceso padre escribe información en una tubería utilizando su manejador de escritura, mientras que el proceso hijo lee la información de la tubería utilizando su manejador de lectura. Similarmente, el proceso hijo escribe información en la otra tubería y el proceso padre lee desde la misma.
Las tuberías anónimas no pueden ser usadas sobre una red, y tampoco pueden ser usadas entre procesos que no estén relacionados.
Tuberías nombradas
Las tuberías nombradas (named pipes) son usadas para transferir datos entre procesos que no estén relacionados e incluso entre procesos que se encuentren en distintas computadoras. Típicamente, un proceso servidor de una tubería nombrada crea la misma con un nombre bien conocido o con un nombre que está hecho para comunicar a sus clientes. Un proceso cliente de una tubería nombrada que conoce el nombre de la tubería puede abrir su otro extremo, sujeto a restricciones de acceso especificadas por el proceso servidor de la tubería. Luego de que tanto el cliente como el servidor se hayan conectado a la tubería, los mismos pueden intercambiar información ejecutando las operaciones de lectura y/o escritura sobre la tubería.
La comunicación por tuberías nombradas consiste en un servidor de tubería nombrada y un cliente de tubería nombrada. Un servidor de tubería nombrada es una aplicación que crea una tubería nombrada a la cual los clientes se pueden conectar. El nombre de una tubería nombrada tiene el formato “\\Server\Pipe\PipeName”. El componente ‘server’ del nombre especifica la computadora en la cual se está ejecutando la tubería nombrada (un servidor de tubería nombrada no puede crear una tubería nombrada en un sistema remoto). El nombre puede ser un nombre de DNS, un nombre de NetBIOS o una dirección IP. El componente ‘pipe’ del nombre debe ser el string “Pipe”, y ‘pipeName’ es el nombre único asignado a una tubería nombrada. Ésta es única porción del nombre de la tubería que puede incluir subdirectorios; un ejemplo de tal sería:
“\\MyComputer\Pipe\MyServerApp\ConnectionPipe”
Un servidor de tubería nombrada utiliza la función de Windows CreateNamedPipe para crear una tubería nombrada. Uno de los parámetros de entrada de la función es un puntero al nombre de dicha tubería, de la forma “\\.\Pipe\PipeName” (‘\\.\’ es un alias definido en Windows para “esta” computadora). Otro de los parámetros que la función acepta incluye un descriptor de seguridad opcional que protege el acceso a la tubería, un flag que especifica si la tubería debería ser bidireccional o unidireccional, un valor que indica el máximo de conexiones simultáneas que la tubería soporta, y un flag que especifica si la tubería debería operar en “byte mode” (Modo byte) o en “message mode” (Modo mensaje).
La mayoría de las APIs para el trabajo en red operan solo en modo byte, lo que significa que un mensaje enviado con una (1) función de envía podría requerir que el receptor realice múltiples recepciones, para construir el mensaje completo a partir de los fragmentos. Las tuberías nombradas que operan en modo mensaje simplifican la implementación del receptor porque hay una correspondencia uno-a-uno entre el envío y la recepción. Un receptor, así, obtiene un mensaje entero cada vez que completa una recepción y no se tiene que preocupar por el seguimiento de los fragmentos del mensaje. La primera invocación de CreateNamedPipe para un nombre particular crea la primera instancia de ese nombre y establece el comportamiento de todas las instancias de la tubería nombrada con aquel nombre. Un servidor crea instancias adicionales, hasta el máximo especificado en la primera invocación, con invocaciones adicionales de CreateNamedPiped. Después de haber creado al menos una instancia de la tubería, un servidor ejecuta la función de Windows ConnectNamedPipe, la cual habilita la tubería nombrada creada por el servidor para establecer conexiones con los clientes. ConnectNamedPipe puede ser ejecutada de manera sincronizada o no, y no se completa hasta que un cliente establezca una conexión con la instancia (u ocurra un error).
Una tubería nombrada usa la función de Windows CreateFile o CallNamedPipe, especificando el nombre de la tubería que un servidor ha creado, para conectarse al servidor. Si el servidor ha realizado una invocación de ConnectNamedPipe, el perfil de seguridad del cliente y el acceso que solicita (lectura, escritura) a la tubería son validados con el descriptor de seguridad de la tubería en cuestión. Si el acceso del cliente a la tubería es concedido, el mismo recibo un manejador que representa el lado del cliente de la conexión a la tubería y la invocación a ConnectNamedPipe por parte del servidor es completada.
Luego que la conexión con la tubería es establecida, el cliente y el servidor pueden usar las funciones de Windows ReadFile y WriteFile para leer y escribir en la tubería, respectivamente. Las tuberías nombradas soportan operaciones sincrónicas y asincrónicas para la transmisión de mensajes.
La siguiente figura muestra un servidor y un cliente comunicándose a través de una instancia de una tubería nombrada:

Una característica única de la API para trabajo en red con tuberías nombradas es que permite a un servidor hacerse pasar (“impersonate”) por un cliente usando la función ImpersonateNamedPipeClient.
Hasta aquí esta primera parte.

