usuarios usuario universitarias una publicas procesos información hacia formativos formación formacion educacion definicion conceptuación bibliotecas biblioteca c++ new-operator delete-operator

c++ - usuario - ¿Cómo controlar la estrategia de asignación de memoria en un código de biblioteca de terceros?



formación de usuarios de la información y procesos formativos hacia una conceptuación (3)

No se puede hacer para la asignación realizada dentro de esa biblioteca de clases, pero puede usar la ubicación nueva para asignar clases desde esa biblioteca de terceros, es decir, puede asignar la memoria y hacer que los constructores de esas clases invoquen la memoria asignada. De esta forma, incluso si la clase su propio nuevo operador no se llamaría .Howvwer, dentro de las asignaciones de memoria de operaciones de clase a clases primitivas o primitivas no expuestas se hará usando el esquema de asignación de la biblioteca de terceros; eso no puede cambiarse a menos que una biblioteca de terceros le permita especificar un asignador como contenedores stl

Encabezado anterior: "¿Debo reemplazar los operadores globales nuevos y eliminar para cambiar la estrategia de asignación de memoria en un código de terceros?"

Breve historia: Necesitamos reemplazar la técnica de asignación de memoria en la biblioteca de terceros sin cambiar su código fuente.

Larga historia:

Considere la aplicación de memoria que hace enormes asignaciones dinámicas (tal vez, casi toda la memoria disponible del sistema). Utilizamos asignadores especializados y los usamos en todas partes ( shared_ptr , contenedores, etc.). Tenemos control total y poder sobre cada byte de memoria asignado en nuestra aplicación.

Además, tenemos que enlazar con una biblioteca auxiliar de terceros . Ese tipo desagradable hace asignaciones de una manera estándar, utilizando operadores predeterminados new , new[] , delete y delete[] o malloc o algo más no estándar (vamos a generalizar y decir que no sabemos cómo esta biblioteca administra su asignación de montón )

Si esta biblioteca de ayuda hace una asignación lo suficientemente grande, podemos obtener problemas de HDD, fragmentación de memoria y alineaciones, bad_alloc s de memoria y todo tipo de problemas.

No podemos (o no queremos) cambiar el código fuente de la biblioteca.

Primer intento:

Nunca antes habíamos tenido tales "hacks" impíos en versiones de lanzamiento. La primera prueba con el operador de redefinición funciona bien, excepto que:

  • no sabemos qué problemas nos esperan en el futuro (y esto es horrible)
  • nuestros usuarios (e incluso nuestros asignativos) ahora tienen que asignar de la misma manera que nosotros

Preguntas:

  1. ¿Hay formas de enganchar estas asignaciones sin sobrecargar a los operadores globales? (¿libs solo local lib?)
  2. ... y si no sabemos exactamente qué usa: malloc o new ?
  3. ¿Esta lista de firmas está completa? (y no hay otras cosas que debemos implementar):

    void* operator new (std::size_t size) throw (std::bad_alloc); void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) throw(); void* operator new (std::size_t size, void* ptr) throw(); void* operator new[] (std::size_t size) throw (std::bad_alloc); void* operator new[] (std::size_t size, const std::nothrow_t& nothrow_value) throw(); void* operator new[] (std::size_t size, void* ptr) throw(); void operator delete (void* ptr) throw(); void operator delete (void* ptr, const std::nothrow_t& nothrow_constant) throw(); void operator delete (void* ptr, void* voidptr2) throw(); void operator delete[] (void* ptr) throw(); void operator delete[] (void* ptr, const std::nothrow_t& nothrow_constant) throw(); void operator delete[] (void* ptr, void* voidptr2) throw();

  4. ¿Algo diferente si esa biblioteca es dinámica?

Editar # 1

La solución multiplataforma es preferible si es posible (parece que no es muy posible). Si no, nuestras principales plataformas:

  • Windows x86 / x64 (msvc 10)
  • Linux x86 / x64 (gcc 4.6)

Editar # 2

Han transcurrido casi 2 años, han evolucionado pocas versiones de sistemas operativos y compiladores, por lo que tengo curiosidad de saber si hay algo nuevo e inexplorado en esta área. ¿Alguna propuesta estándar? ¿Específicos del sistema operativo? Hacks? ¿Cómo escribes aplicaciones sedientas de memoria hoy? Por favor comparte tu experiencia.


Sin poder modificar el código fuente de la biblioteca, o mejor, poder influir en el autor de la biblioteca para modificarlo, diría que no tiene suerte.

Hay algunas cosas que la biblioteca puede hacer potencialmente (incluso de manera no intencional) para hacerla inmune a cualquier estrategia que pueda emplear, o, en el peor de los casos, puede hacer que la biblioteca sea inestable o que el programa sea inestable. Como usar sus propios asignadores personalizados, proporcionando sus propias versiones del operator new() global operator new() y operator delete() , anulando esos operadores en clases individuales, etc.

Una estrategia que probablemente funcione es trabajar con el proveedor de la biblioteca y hacer algunas modificaciones. Las modificaciones (de su parte) equivaldrían a poder inicializar la biblioteca especificando los asignadores que utiliza. Para la biblioteca, el esfuerzo es potencialmente significativo (al tener que tocar todas las funciones que asignan dinámicamente memoria, que usan contenedores estándar, etc.) pero no es intratable: use los asignadores proporcionados (o valores predeterminados razonables) en todo su código.

Desafortunadamente, eso está en desacuerdo con su requisito de no modificar la biblioteca: soy escéptico de las posibilidades de satisfacer eso, particularmente dentro de las restricciones que ha descrito (sediento de memoria, alojado en Windows / Linux, etc.).


Uf, mi simpatía. Esto va a depender mucho de tu compilador, tu libc, etc. Algunas estrategias de "rubber-meets-road" que han "funcionado" en diversos grados para nosotros en el pasado (/ me refuerzos para downvotes) son:

  • Las sobrecargas operator delete operator new / operator delete sugeridas, aunque tenga en cuenta que algunos compiladores son exigentes con las especificaciones de throw() , algunos realmente los quieren, algunos los quieren para nuevos pero no para eliminar, etc. (Tengo una plataforma gigante específica #if / #elif para todas las 4+ plataformas en las que estamos trabajando ahora).
  • También vale la pena señalar: generalmente puede ignorar las versiones de ubicación, que no asignan.
  • Mire __malloc_hook y sus amigos , tenga en cuenta que están en desuso y tienen condiciones de carrera de subprocesos, pero son agradables porque los nuevos / eliminados tienden a implementarse en términos de malloc (pero no siempre).
  • Proporcionar un reemplazo de malloc , calloc , realloc y free y hacer que su enlazador args en el orden correcto para que ocurran las anulaciones (esto es lo que recomienda gcc en estos días, aunque he tenido situaciones en las que era imposible hacerlo, y tuvo que usar __malloc_hook desuso) - de nuevo, el new y el delete tienden a ser implementados en términos de estos, pero no siempre.
  • Evitar todos los métodos de asignación estándar ( operator new , malloc , etc.) en "nuestro código" y usar funciones personalizadas, lo que no es muy fácil con la base de código existente.
  • Rastreando al autor de la biblioteca y entregando un parche o solicitud cortés de golpe salvaje para cambiar su biblioteca y permitirte especificar un asignador diferente (puede ser más rápido que hacerlo tú mismo) - Creo que esto ha llevado a una regla cardinal de "cliente" siempre especifica el asignador o la asignación "con las bibliotecas que escribo.

Tenga en cuenta que esta no es una respuesta en términos de lo que los estándares dicen que debería suceder, solo mi experiencia. He trabajado con más de unos pocos compiladores fallidos / descompuestos e implementaciones de libc en el pasado, por lo que YMMV. También tengo el lujo de trabajar en sistemas bastante "sellados", y no estoy tan preocupado por la portabilidad para cualquier aplicación específica.

En cuanto a las bibliotecas dinámicas: actualmente estoy un poco pellizcado en este sentido; nuestra "aplicación" se carga como .so dinámica y tenemos que tener mucho cuidado de pasar cualquier solicitud de delete / free al asignador predeterminado si no vinieron de nosotros. La solución actual es acordonar nuestras asignaciones a un área específica: si obtenemos una eliminación / libre dentro de ese rango de direcciones, enviamos a nuestro controlador, de lo contrario volvemos a la predeterminada ... incluso he jugado con (horrores ) la idea de verificar la dirección de la persona que llama para ver si está en nuestro espacio de direcciones. (Sin embargo, la probabilidad de ir al auge aumenta con tales ataques).

Esta puede ser una estrategia útil, incluso si usted es el líder del proceso y está usando una biblioteca externa: marque o restrinja o identifique de alguna manera sus propios alloc de alguna manera (incluso yendo tan lejos como para mantener una lista de los allocs que conoce), y luego pasa cualquier incógnito. Sin embargo, todo esto tiene efectos secundarios y limitaciones desagradables.

(¡Esperamos otras respuestas!)