2012-05-30 20 views
16

Soy nuevo en la programación de Windows y acabo de "perder" dos horas buscando un error que todos parecen conscientes: no puede crear un objeto en el montón en una DLL y destruirlo en otra DLL (o en general) programa).¿Las DLL (estáticamente vinculadas) usan un montón diferente que el programa principal?

Estoy casi seguro de que en Linux/Unix este NO es el caso (si es así, dígalo, pero estoy seguro de que lo hice miles de veces sin problemas ...).

En este momento tengo un par de preguntas:

1) No usar DLL vinculados estáticamente un montón diferente que el programa principal?

2) ¿Está la DLL enlazada estáticamente asignada en el mismo espacio de proceso del programa principal? (Estoy bastante seguro de que la respuesta aquí es un gran SÍ, de lo contrario no tendría sentido pasar punteros desde una función en el programa principal a una función en una DLL).

Estoy hablando de DLL normal/regular, no los servicios/ATL COM

EDIT: Por "enlazado estáticamente" quiero decir que yo no uso LoadLibrary para cargar el archivo DLL pero enlazar con la biblioteca de código auxiliar

+3

Depende de la configuración de cada módulo. En general, si dos módulos utilizan el CRT dinámico, entonces comparten el montón, ya que ambos tienen la misma instancia del CRT cargado. Si un módulo usa el CRT estático, entonces tiene su propia pila, ya que tiene su propia instancia del CRT enlazado estáticamente. – Luke

+1

@Luke - Además, es posible que diferentes módulos utilicen diferentes versiones del CRT dinámico (DLL), y por lo tanto diferentes montones. – Bukes

+0

Casi todas las DLL no triviales tendrían que_ crear su propio montón, si lo piensas bien. Tome la biblioteca OpenAL como un ejemplo. Puede alimentar datos a un objeto buffer (la lib crea su propia copia de los datos), establecer algunos parámetros y la biblioteca reproducirá el sonido: genial, fácil, perfecto, sin preocupaciones. Ahora imagine que dos programas cargan la biblioteca. Dónde colocar los datos, ¿a quién pertenece? ¿En qué parte de la RAM física es? ¿Quiero que "algún otro programa" pueda ver (o modificar) los datos en el montón de mi programa? Si vive en el montón de tu módulo principal, estás en problemas ... – Damon

Respuesta

16

DLLs/exes necesitarán vincular a una implementación de bibliotecas de tiempo de ejecución de C.

En caso de bibliotecas de C de Windows en tiempo de ejecución, usted tiene la opción de especificar, si desea vincular a lo siguiente:

  1. biblioteca de tiempo de un único subproceso de ejecución C (Apoyo a las bibliotecas un solo subproceso se han suspendido ahora)
  2. DLL de subprocesos múltiples/DLL de depuración de subprocesos múltiples
  3. Biblioteca de tiempo de ejecución estática.
  4. Algunos más (Puede comprobar el enlace)

Cada uno de ellos se refería a un montón diferente, por lo que no está permitido dirección de pase obtiene a partir de un montón de biblioteca en tiempo de ejecución a otra.

Ahora, depende de a qué biblioteca de tiempo de ejecución de C se ha vinculado la DLL a la que se refiere. Supongamos que el DLL que está utilizando se ha vinculado a la biblioteca de tiempo de ejecución de C estática y el código de la aplicación (que contiene la función principal) se ha vinculado a C Runtime DLL de subprocesos múltiples y, a continuación, si pasa un puntero a la memoria asignada en el DLL a su programa principal y trate de liberarlo allí o viceversa, puede conducir a un comportamiento indefinido. Entonces, la causa raíz básica son las bibliotecas C runtime. Por favor, elíjalos con cuidado.

Puede encontrar más información sobre las bibliotecas de tiempo de ejecución de C soportados here & here

Una cita de MSDN:

PrecauciónNo mezcle versiones estáticas y dinámicas de las bibliotecas de tiempo de ejecución. Tener más de una copia de las bibliotecas en tiempo de ejecución en un proceso puede causar problemas, porque los datos estáticos en una copia no se comparten con la otra copia. El vinculador le impide vincularse con versiones estáticas y dinámicas dentro de un archivo .exe, pero aún puede terminar con dos (o más) copias de las bibliotecas de tiempo de ejecución.Por ejemplo, una biblioteca de enlace dinámico vinculada con las versiones estáticas (no DLL) de las bibliotecas en tiempo de ejecución puede causar problemas cuando se utiliza con un archivo .exe que se vinculó con la versión dinámica (DLL) de las bibliotecas en tiempo de ejecución . (También debe evitar mezclar las versiones de depuración y no depuración de las bibliotecas en un proceso).

+0

Estoy hablando de cualquier tipo de DLL, no necesariamente las que contienen el código de tiempo de ejecución de C – Emiliano

+1

¿Por qué cada variante de RTL debería usar diferente montón? – osgx

+0

Comprendo el problema ahora. Pero pasar un puntero a través de los límites del módulo debería ser seguro, ¿no? (siempre que el objeto no haya sido destruido mientras tanto, por supuesto y mientras el programa principal y la DLL estén compilados con el mismo compilador) – Emiliano

3

Si tengo una aplicación que compila como .exe y quiero usar una biblioteca, puedo vincular estáticamente eso biblioteca desde un archivo .lib o enlazado dinámicamente esa biblioteca desde un archivo .dll.

Cada módulo vinculado (es decir, cada .exe o .dll) se vinculará a una implementación de los tiempos de ejecución C o C++. Los tiempos de ejecución son una biblioteca que se puede vincular estática o dinámicamente y vienen en diferentes configuraciones de subprocesamiento.

Al decir dlls estáticamente vinculados ¿está describiendo una configuración en la que una aplicación .exe se vincula dinámicamente a un archivo .dll de la biblioteca y esa biblioteca enlaza internamente estáticamente con el tiempo de ejecución? Asumiré que esto es lo que quieres decir.

También vale la pena señalar que cada módulo (.exe o .dll) tiene su propio alcance para estáticas, es decir, una estática global en un .exe no será la misma instancia que una estática global con el mismo nombre en un .dll .

En el caso general, por lo tanto, no puede suponerse que el código que se ejecuta dentro de los diferentes módulos esté utilizando la misma implementación del tiempo de ejecución, además, no usarán la misma instancia de ningún estado estático.

Por lo tanto, se deben cumplir ciertas reglas cuando se trata de objetos o punteros que cruzan los límites del módulo. Las asignaciones y desasignaciones deben ocurrir en el mismo módulo para cualquier dirección dada. De lo contrario, los montones no coincidirán y el comportamiento no se definirá.

COM soluciona esto utilizando el recuento de referencias, los objetos se eliminan cuando el recuento de referencias llega a cero. Este es un patrón común utilizado para resolver el problema de ubicación coincidente.

Pueden existir otros problemas, por ejemplo, windows define ciertas acciones, p. cómo se manejan las fallas de asignación por subproceso, no por cada módulo. Esto significa que el código que se ejecuta en el módulo A en una secuencia configurada por el módulo B también puede tener un comportamiento inesperado.

2

Primero, comprendamos la asignación de montones y apilamos en el sistema operativo Windows nuestras aplicaciones/DLL. Tradicionalmente, el sistema operativo y las bibliotecas en tiempo de ejecución vienen con una implementación del montón.

  1. Al comienzo de un proceso, el sistema operativo crea un montón predeterminado llamado montón de procesos. El montón de proceso se usa para asignar bloques si no se usa ningún otro montón.
  2. Los tiempos de ejecución del lenguaje también pueden crear montones separados dentro de un proceso. (Por ejemplo, el tiempo de ejecución de C crea un montón).
  3. Además de estos montones dedicados, el programa de aplicación o una de las muchas bibliotecas de vínculos dinámicos cargados (DLL) pueden crear y usar montones separados, llamados montones privados
  4. Este montón se encuentra en la parte superior del Administrador de memoria virtual del sistema operativo en todos los sistemas de memoria virtual.
  5. Vamos a discutir más sobre la CRT y montones asociados:
    1. C/C++ en tiempo de ejecución (CRT) asignador: Proporciona malloc() y free(), así como operadores new y delete.
    2. El CRT crea un montón tan extra para todas sus asignaciones (el controlador de este almacenamiento CRT se almacena internamente en la biblioteca CRT en una variable global llamada _crtheap) como parte de su inicialización.
    3. CRT crea su propio montón privado, que reside en la parte superior del montón de Windows.
    4. El montón de Windows es una capa delgada que rodea el asignador de tiempo de ejecución de Windows (NTDLL).
    5. El asignador de tiempo de ejecución de Windows interactúa con el Asignador de memoria virtual, que reserva y compromete las páginas utilizadas por el sistema operativo.

Su DLL y EXE enlace a las bibliotecas estáticas CRT multiproceso. Cada archivo DLL y archivo EXE que crea tiene su propia pila, es decir, _crtheap. Las asignaciones y desasignaciones tienen que suceder desde el montón respectivo. Que una DLL dinámicamente asignada no se puede desasignar desde el ejecutable y viceversa.

¿Qué puedes hacer? Compile nuestro código en DLL y exe utilizando/MD o/MDd para usar la versión específica de múltiples hilos y específica de DLL de la biblioteca en tiempo de ejecución. Por lo tanto, DLL y EXE están vinculados a la misma biblioteca de tiempo de ejecución de C y, por lo tanto, a un _crtheap. Las asignaciones siempre están emparejadas con las desasignaciones dentro de un solo módulo.

+0

Aunque en Linux, no estoy seguro de qué ocurre cuando vincula la biblioteca de tiempo de ejecución de Static C con su DLL (SharedLibrary) y exe? ¿Allí también enfrentaremos el mismo problema? –

Cuestiones relacionadas