windows - ¿Las DLL(vinculadas estáticamente) utilizan un montón diferente al del programa principal?
linux heap (3)
Soy nuevo en la programación de Windows y acabo de "perder" dos horas buscando un error que todos parecen conocer: no puede crear un objeto en el montón en una DLL y destruirlo en otra DLL (o en el programa principal) .
Estoy casi seguro de que en Linux / Unix este NO es el caso (si es así, por favor, dígalo, pero estoy bastante seguro de que lo hice miles de veces sin problemas ...).
En este punto tengo un par de preguntas:
1) ¿Las DLL vinculadas estáticamente usan un montón diferente al del programa principal?
2) ¿Se asigna la DLL enlazada estáticamente 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 los punteros desde una función en el programa principal a una función en una DLL).
Estoy hablando de DLL simple / regular, no de servicios COM / ATL
EDITAR: Por "estáticamente vinculado" quiero decir que no uso LoadLibrary para cargar la DLL, sino que vinculo con la biblioteca de código auxiliar
Los DLL / exes deberán vincularse a una implementación de bibliotecas de tiempo de ejecución de C.
En el caso de las bibliotecas C Windows Runtime, tiene la opción de especificar, si desea enlazar a lo siguiente:
- Biblioteca de tiempo de ejecución de C de un solo hilo (el soporte para bibliotecas de un solo hilo se ha suspendido ahora)
- DLL multihilo / DLL de depuración multihilo
- Bibliotecas estáticas de tiempo de ejecución.
- Pocos más (puedes consultar el enlace)
Cada uno de ellos se referirá a un montón diferente, por lo que no se le permite la dirección de paso obtenida del montón de una biblioteca de tiempo de ejecución a otra.
Ahora, depende de a qué biblioteca de tiempo de ejecución C se haya vinculado la DLL de la que está hablando. Supongamos que, digamos, la DLL que está utilizando se ha vinculado a la biblioteca de tiempo de ejecución de C estática y el código de su aplicación (que contiene la función principal) se ha vinculado a la DLL de tiempo de ejecución de C multiproceso, si pasa un puntero a la memoria asignada en el DLL a su programa principal e intente liberarlo allí o viceversa, puede llevar a un comportamiento indefinido. Por lo tanto, la causa raíz básica son las bibliotecas de tiempo de ejecución de C. Por favor elíjalos con cuidado.
Encuentre más información sobre las bibliotecas de tiempo de ejecución de C compatibles here y here
Una cita de MSDN:
Precaución No mezcle versiones estáticas y dinámicas de las bibliotecas en 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 enlazador evita que se vincule con versiones tanto estáticas como dinámicas dentro de un archivo .exe, pero aún puede terminar con dos (o más) copias de las bibliotecas en tiempo de ejecución. Por ejemplo, una biblioteca de vínculos dinámicos vinculada con las versiones estáticas (no DLL) de las bibliotecas de tiempo de ejecución puede causar problemas cuando se usa con un archivo .exe que se vinculó con la versión dinámica (DLL) de las bibliotecas de tiempo de ejecución . (También debe evitar mezclar las versiones de depuración y no depuración de las bibliotecas en un proceso).
Primero entendamos la asignación de pilas y la pila en el sistema operativo Windows y nuestras aplicaciones / DLL. Tradicionalmente, el sistema operativo y las bibliotecas de tiempo de ejecución vienen con una implementación del montón.
- Al comienzo de un proceso, el sistema operativo crea un montón predeterminado denominado Procesar montón. El montón de proceso se utiliza para asignar bloques si no se utiliza ningún otro montón.
- Los tiempos de ejecución del idioma también pueden crear montones separados dentro de un proceso. (Por ejemplo, el tiempo de ejecución de C crea un montón propio).
- Además de estos montones dedicados, el programa de aplicación o una de las muchas bibliotecas de enlaces dinámicos (DLL) cargadas puede crear y usar montones separados, llamados montones privados.
- Estos montones se ubican sobre el Administrador de memoria virtual del sistema operativo en todos los sistemas de memoria virtual.
- Vamos a discutir más sobre CRT y montones asociados:
- Asignador de tiempo de ejecución de C / C ++ (CRT): proporciona malloc () y free (), así como operadores nuevos y de eliminación.
- El CRT crea un montón adicional para todas sus asignaciones (el identificador de este montón de CRT se almacena internamente en la biblioteca de CRT en una variable global llamada _crtheap) como parte de su inicialización.
- CRT crea su propio montón privado, que reside en la parte superior del montón de Windows.
- El montón de Windows es una capa delgada que rodea al asignador de tiempo de ejecución de Windows (NTDLL).
- El asignador de tiempo de ejecución de Windows interactúa con el Asignador de memoria virtual, que reserva y confirma las páginas utilizadas por el sistema operativo.
Su archivo DLL y exe enlazan a bibliotecas CRT estáticas multiproceso. Cada DLL y exe que creas tiene su propio montón, es decir, _crtheap. Las asignaciones y desasignaciones tienen que suceder desde el montón respectivo. Que una asignación dinámica de DLL, no puede ser desasignada de ejecutable y viceversa.
¿Lo que puedes hacer? Compile nuestro código en DLL y exe utilizando / MD o / MDd para usar la versión de multiproceso y específica de DLL de la biblioteca en tiempo de ejecución. Por lo tanto, tanto DLL como exe están vinculados a la misma biblioteca de tiempo de ejecución de C y, por lo tanto, a una _crtheap. Las asignaciones siempre se emparejan con desasignaciones dentro de un solo módulo.
Si tengo una aplicación que compila como un archivo .exe y quiero usar una biblioteca, puedo vincular estáticamente esa biblioteca desde un archivo .lib o vincular 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 de C o C ++. Los tiempos de ejecución en sí mismos son una biblioteca que se puede vincular de forma estática o dinámica a diferentes configuraciones de subprocesos.
Al decir dlls estáticamente vinculados, ¿está describiendo una configuración en la que una aplicación .exe se vincula dinámicamente con una biblioteca .dll y esa biblioteca se vincula de forma estática interna al tiempo de ejecución? Supondré que esto es lo que quieres decir.
También es de destacar que cada módulo (.exe o .dll) tiene su propio ámbito para las estadísticas, es decir, una estática global en un .exe no será la misma instancia que una estática global con el mismo nombre en una .dll.
En el caso general, por lo tanto, no se puede suponer que las líneas de código que se ejecutan dentro de diferentes módulos estén 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, ciertas reglas deben ser obedecidas cuando se trata de objetos o punteros que cruzan los límites de los módulos. 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 resuelve esto utilizando el conteo de referencias, los objetos se eliminan cuando el conteo de referencia 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, por ejemplo, cómo se manejan las fallas de asignación por subproceso, no por módulo. Esto significa que el código que se ejecuta en el módulo A en una configuración de subprocesos por el módulo B también puede tener un comportamiento inesperado.