2011-01-06 13 views
9

Me preguntaba cómo compartir algo de memoria entre diferentes módulos de programa, digamos, tengo una aplicación principal (exe) y luego algún módulo (dll). Ambos se vinculan a la misma biblioteca estática. Esta biblioteca estática tendrá un administrador que brinda varios servicios. Lo que me gustaría lograr es tener este administrador compartido entre todos los módulos de la aplicación y hacerlo de forma transparente durante la inicialización de la biblioteca. Entre los procesos podría usar la memoria compartida, pero quiero que esto se comparta solo en el proceso actual. ¿Podría pensar en alguna forma de plataforma cruzada para hacer esto? Posiblemente usando bibliotecas de impulso, si proporcionan algunas facilidades para hacer esto.Compartiendo memoria entre módulos

La única solución en la que puedo pensar ahora, es usar la biblioteca compartida del sistema operativo respectivo, que todos los demás módulos vincularán en el tiempo de ejecución, y que el administrador guarde allí.

EDIT: para aclarar lo que realmente se necesita:

  1. necesito para averiguar si ya se ha creado el gestor compartido (las respuestas a continuación ya previstas algunas maneras de hacer eso)
  2. Consigue la puntero al administrador, si existe, o establecer el puntero en algún lugar para el objeto administrador recientemente creado.
+0

Dado que este es un proceso único, ¿cómo es esto diferente de un patrón de singleton normal? – sdg

+0

De hecho, es un singleton, el problema es cómo implementarlo, por lo que se compartirá entre diferentes bibliotecas compartidas. –

+0

Dentro del mismo proceso, cualquier módulo puede acceder a variables globales externas sin ninguna restricción. – 9dan

Respuesta

11

Creo que vas a necesitar ayuda de una biblioteca compartida para hacer esto de cualquier manera portátil. No necesariamente necesita saber nada sobre los objetos que se comparten entre los módulos, solo tiene que proporcionar una asignación accesible desde una clave (probablemente una cadena) a un puntero.

Sin embargo, si está dispuesto a llamar a las API de OS, esto es factible, y creo que puede necesitar solo dos implementaciones de la parte específica del sistema operativo (una para Windows DLL y GetProcAddress, una para SO que usan dlopen) .

A medida que se carga cada módulo, recorre la lista de módulos cargados previamente en busca de alguno que exporte una función especialmente nombrada. Si encuentra uno (cualquiera, no importa cuál, porque el invariante es que todos los módulos totalmente cargados conocen el objeto común), obtiene la dirección del objeto común del módulo previamente cargado, luego incrementa el recuento de referencias . Si no puede encontrar ninguno, asigna nuevos datos e inicializa el recuento de referencias. Durante la descarga del módulo, disminuye el recuento de referencias y libera el objeto común si el recuento de referencias llegó a cero.

Por supuesto, es necesario utilizar el asignador de SO para el objeto común, porque aunque es poco probable, es posible que se desasigne de una biblioteca diferente a la que primero la cargó. Esto también implica que el objeto común no puede contener ninguna función virtual ni ningún otro tipo de puntero a los segmentos de los diferentes módulos. Todos sus recursos deben asignarse dinámicamente utilizando el asignador de todo el proceso del sistema operativo. Probablemente esta sea una carga menor en los sistemas en los que libC++ es una biblioteca compartida, pero usted dijo que está conectando estáticamente el CRT.

funciones necesarias en Win32 incluiría EnumProcessModules, GetProcAddress, HeapAlloc, y HeapFree, GetProcessHeap y GetCurrentProcess.

Todo considerado, creo que me quedaría con poner el objeto común en su propia biblioteca compartida, que aprovecha las estructuras de datos del cargador para encontrarlo. De lo contrario, estás reinventando el cargador. Esto funcionará incluso cuando el CRT esté enlazado estáticamente en varios módulos, pero creo que se está preparando para violaciones de ODR. Sea realmente particular acerca de mantener el POD de datos común.

+0

Bueno, esa es una solución realmente creativa :-) Me pregunto si el ejecutable de la aplicación vincula a algunas bibliotecas compartidas (automáticamente al inicio), y al mismo tiempo tiene algunas variables globales definidas, es el orden de carga de las bibliotecas/inicialización global variables definidas por el estándar, o se define su implementación? Si los datos globales se inicializan primero, entonces podemos realizar la inicialización en el ejecutable principal y no preocuparnos por los diferentes asignadores de memoria en el caso de los enlaces CRT estáticos. –

+1

@John: ¿Están garantizando que estas variables existen en el ejecutable principal? Pensé que estabas diseñando para el caso general: tratando de construir una biblioteca que funcione si la aplicación usa el mismo objeto auxiliar o no. De todos modos, las bibliotecas no son tratadas por el estándar C++ AT ALL, por lo que específicamente el orden de carga no está definido por el estándar. Sin embargo, en la mayoría de las plataformas, las bibliotecas son manejadas por el cargador del sistema operativo antes de ejecutar cualquier constructor.Por supuesto, con carga de retraso o carga explícita las bibliotecas no se cargan durante el inicio, sino cuando se ejecuta ese código. –

+0

Sí, se supone que la biblioteca es genérica, por lo que fue solo un pensamiento. –

0

Puede utilizar boost::interprocesshttp://www.boost.org/doc/libs/1_45_0/doc/html/interprocess.html y en Windows puede crear un segmento compartido en el archivo DLL que será compartido por todos los procesos que utilizan # de Pragma: http://www.codeproject.com/KB/DLL/data_seg_share.aspx

+0

Sí, espero que pueda resolverse con boost, pero ¿podría ser más específico cómo crear un segmento de memoria con nombre en el proceso actual solamente? –

+0

¿Qué quiere decir con "proceso actual solamente"? Accesible desde un solo proceso? Escribir desde un solo proceso? –

+0

Si creo una memoria compartida con alguna ID, será accesible desde todos los procesos en el sistema, ¿verdad? Necesito que la memoria sea accesible solo por el proceso que la crea, con acceso total de lectura/escritura –

1

Para el uso del proceso actual, en que Don No es necesario idear ninguna función o estructura especial.

Puede hacerlo incluso sin ninguna función, pero es más seguro y compatible con varias plataformas para definir el conjunto de funciones que proporcionan acceso a los datos compartidos. Y estas funciones podrían ser implementadas por la biblioteca estática común.

Creo que la única preocupación de esta configuración es la siguiente: "¿Quién será el propietario de los datos?". Debe existir un único propietario de los datos compartidos.

Con estos idea básica, que podría esbozar la API de la siguiente manera:

IsSharedDataExist  // check whether of not shared data exist 

CreateSharedData  // create (possibly dynamically) shared data 

DestroySharedData  // destroy shared data 

... various data access API ... 

o una clase de C++ con el patrón Singleton será apropiado.


ACTUALIZACIÓN

yo estaba confundido. El problema real se puede definir como "Cómo implementar una clase Singleton en una biblioteca estática que se vinculará con una biblioteca de carga dinámica múltiple (se usará en el mismo proceso) de manera independiente de la plataforma".

Creo que la idea básica no es muy diferente, pero asegúrese de que el singleton es el único problema adicional de esta configuración.

Para este propósito, puede emplear Boost.Interprocess.

#include <boost/config.hpp> 
#include <boost/interprocess/sync/named_mutex.hpp> 
... 
boost::interprocess::named_mutex* singleton_check = 0; 

// in the Create function of the singleton 
try { 
    singleton_check = new boost::interprocess::named_mutex(boost::interprocess::create_only, "name_of_the_mutex"); 
    // if no exception throw, this is the first time execution 
} 
catch (...) 
{ 
} 

Liberar el named_mutex es tan simple como delete singleton_check.


ACTUALIZACIÓN # 2

Otra sugerencia.

Creo que no deberíamos colocar datos compartidos en la biblioteca estática común. Si no podemos garantizar datos globalmente únicos, no solo se trata de problemas de implementación que dependen de la plataforma, sino también de desperdicio de memoria y recursos globales.

Si prefiere la implementación de la biblioteca estática, debe crear dos bibliotecas estáticas. Uno para el servidor/creador de los datos compartidos, uno para los usuarios de los datos compartidos. La biblioteca del servidor define y proporciona acceso al Singleton. La biblioteca del cliente proporciona varios métodos de acceso a datos.

Esto es efectivamente igual que la implementación de Singleton sin bibliotecas estáticas.

+0

La forma en que iba a abordar esto, era buscar la memoria nombrada; si ya existía, la accedería y haría cualquier trabajo que necesite, y si no existía, entonces lo crearía. Lo mismo ocurre con la desinicialización: cada módulo se registraría, para rastrear el recuento de usos, y el último cerrado realizará la limpieza. –

+0

Si es algo entre procesos, necesita una funcionalidad especial en la plataforma. Pero si está "dentro del proceso actual", las variables definidas globalmente son la memoria nombrada. – 9dan

+0

¿Las variables globales no se compartirían en un solo módulo, con las copias separadas en otros módulos? –

0

Según MSDN veo sólo hay dos maneras de compartir datos entre módulos

  1. Uso data_seg pragma
  2. uso de memoria compartida.

Como alguien señaló, Shared Segment funciona solo para dos instancias del mismo dll, por lo que nos queda solo una opción para usar la técnica de Archivos de asignación de memoria.

Cuestiones relacionadas