¿Qué hay de malo en vincular estáticamente el STL en varias bibliotecas compartidas?

8

Aquí está el escenario:

  1. libA.so y libB.so están vinculados estáticamente al mismo STL.
  2. libA.so tiene una API pública que devuelve un std :: string.
  3. libB.so llama a esta función y recibe una copia de la cadena.
  4. Cuando la copia de la cadena libB.so sale del alcance, se llama al destructor de la cadena.
  5. La aplicación seg falla al intentar liberar la cadena copiada.

He leído en otros lugares que vincular estáticamente como este es malo, pero me gustaría entender mejor por qué es malo. ¿Alguien puede explicar por qué la secuencia anterior se bloquearía?

user1509041
fuente

Respuestas:

8

Pensemos en esto con mucho cuidado. libA.so está estáticamente vinculado con el STL. Sin embargo, el STL no existe de forma aislada, requiere el tiempo de ejecución C (CRT). Ambos residen en libstdc ++. Una biblioteca estática. Esto significa que libA.so y libB.so tienen estructuras de datos CRT separadas. En particular, el montón usado por libA.so es diferente del montón usado por libB.so. Asignar una cadena del montón de tiempo de ejecución de libA e intentar liberarse del tiempo de ejecución de libB simplemente no funcionará porque el tiempo de ejecución de libB no tiene registros de asignación de la cadena. La única forma de destruir correctamente la cadena es llamando al destructor dentro de libA.so.

Uno podría preguntar: pero libB.so recibe una copia de la cadena, ¿verdad? Sí, eso es correcto, pero ¿quién ha asignado esta copia? Se ha asignado utilizando el constructor de copia dentro del contexto del tiempo de ejecución de libA.

Dicho esto, aún puede usar la cadena de libB.so. Simplemente no puedes destruirlo desde allí.

También puede permitir que libB reciba un puntero a la cadena y luego cree una copia dentro del contexto del tiempo de ejecución de libB. Esa copia puede ser destruida por libB.

Y es por eso que el enlace estático a veces es malo.

Hadi Brais
fuente
1
No estoy claro a qué "cadena" se refiere aquí. Si se habla de a, std::stringentonces el problema simplemente no existe: o libB.sorecibe una copia de la cadena, con la memoria administrada en su propia memoria, o recibirá una referencia / puntero a la cadena libA.soy no intentará eliminar la cadena de su propia memoria.
Konrad Rudolph el
1
@KonradRudolph Entre otras, las optimizaciones de (N) RVO crean situaciones en las que libA.so construye la cadena devuelta y libB.so la destruye.
Sjoerd el
Esta respuesta es consistente con lo que he visto en la práctica, así que supongo que la parte que no entiendo completamente es cómo funciona el montón para cada biblioteca. Inicialmente, pensé que el montón era algo global singular en el proceso, pero la forma en que lo estás describiendo hace que parezca que hay muchos montones. ¿Es solo una cuestión de cómo se implementan los asignadores (en libc?) O hay algo más que controle esto (como el cargador)?
user1509041
Si hay un montón global o varios montones en un proceso depende de la implementación específica de CRT que se use. He visto ambos casos. Parece que tienes muchos montones. El cargador no tiene nada que ver con montones.
Hadi Brais el
1
@AymanSalah Sí, eso debería funcionar bien. La versión del sistema operativo solo es importante cuando utiliza API específicas del sistema operativo en lugar de las API estándar de idioma.
Hadi Brais
3

El STL se llama "lleno de estado" (como opuesto a "sin estado"), lo que significa que tiene algunas cosas estáticas en su interior. Cuando vincula STL estáticamente a libA.so y libB.so, obtiene dos instancias de la biblioteca STL en la memoria en tiempo de ejecución (con dos copias de material estático). Cada una de esas dos copias administra los recursos asignados de forma independiente y los recursos asignados en una instancia de la biblioteca no se pueden liberar en otra

mvidelgauz
fuente