2010-01-07 11 views
11

Me gustaría controlar el uso de mallocs y liberar en una aplicación utilizando malloc y ganchos libres.usando ganchos gliloc malloc de forma segura para hilos

Aquí está la documentación http://www.gnu.org/s/libc/manual/html_node/Hooks-for-Malloc.html

Desde la página de ejemplo se puede ver que my_malloc_hook cambia de forma transitoria el gancho de leva malloc (o al gancho anterior en la cadena) antes de volver a invocar malloc.

Esto es un problema cuando se monitorean aplicaciones de subprocesos múltiples (consulte el final de la pregunta para obtener una explicación).

Otros ejemplos del uso de Malloc hook que he encontrado en Internet tienen el mismo problema.

¿Hay alguna forma de volver a escribir esta función para que funcione correctamente en una aplicación de subprocesos múltiples?

Por ejemplo, hay una función de libc interna que puede invocar el gancho malloc que completa la asignación, sin la necesidad de desactivar mi gancho.

No puedo ver el código fuente de la libc debido a la política legal corporativa, por lo que la respuesta puede ser obvia.

Mi especificación de diseño dice que no puedo reemplazar malloc con un diseño diferente de malloc.

Puedo suponer que no hay otros ganchos en juego.


ACTUALIZACIÓN

Desde el gancho malloc se retira temporalmente mientras el servicio de la malloc, otro hilo puede llamar a malloc y no obtener el gancho.

Se ha sugerido que malloc tiene un gran bloqueo que impide que esto ocurra, pero no está documentado, y el hecho de que llamo de manera recursiva malloc sugiere que cualquier bloqueo debe existir después del gancho, o ser ingenioso :

caller -> 
    malloc -> 
    malloc-hook (disables hook) -> 
     malloc -> # possible hazard starts here 
     malloc_internals 
     malloc <- 
    malloc-hook (enables hook) <- 
    malloc 
caller 
+0

Si uno de nosotros examina el origen de la libc y le proporciona información basada en él, se encontrará legalmente en el mismo puesto. –

+0

¿Por qué no puedes * mirar * el código fuente de la libc? – Will

+0

Porque podría contaminar nuestro código propietario con el código GPL. El simple hecho de que me digan que una función en particular hará lo que quiero no tiene ese problema. –

Respuesta

8

ACTUALIZADO

Estás right para no confiar en __malloc_hooks; He echado un vistazo al código, y son - asombrosamente loco - no es seguro para subprocesos.

Invocar los ganchos heredados directamente, en lugar de restaurar y reingresar malloc, parece desviarse del documento que cita un poco demasiado para que le resulte cómodo sugerir.

De http://manpages.sgvulcan.com/malloc_hook.3.php:

variables de gancho no son seguros para subprocesos por lo que ahora están en desuso. Los programadores deberían, en cambio, adelantarse a las llamadas a las funciones relevantes definiendo y exportando funciones como "malloc" y "free".

La forma apropiada para inyectar REALLOC/funciones de depuración malloc/libre, es suministrar su propia biblioteca que exporta sus versiones 'depuración' de estas funciones, y luego se remite en sí a los reales. El enlace C se realiza en orden explícito, de modo que si dos bibliotecas ofrecen la misma función, se utiliza la primera especificada. También puede inyectar su malloc en tiempo de carga en Unix utilizando los mecanismos LD_PRELOAD.

http://linux.die.net/man/3/efence describe Electric Fence, que detalla estos dos enfoques.

Puede usar su propio bloqueo si está en estas funciones de depuración si es necesario.

+0

La pregunta es probablemente: ¿Se adquirirá la cerradura antes de que se llame al gancho o pasa eso dentro de malloc()? Supongo que los ganchos serían inútiles sin el bloqueo que ocurre afuera, pero me pregunto cómo funciona la llamada recursiva, entonces. –

+0

La llamada recursiva podría funcionar con bloqueos recursivos: una vez que el hilo tiene el bloqueo, puede adquirirlo varias veces. – Novelocrat

+0

Bueno, eso puede ser cierto, pero a menos que sepa que es verdad no puedo usarlo, ya que puede romperse. Tampoco puedo decir si una implementación futura de malloc puede permitir múltiples zonas malloc con bloqueos separados para mejorar el rendimiento de subprocesos múltiples. –

2

Dado que todas las llamadas a malloc() pasará por su gancho, puede sincronizar en un semáforo (espere hasta que esté libre, bloquearlo, hacer malabares con los ganchos y liberar el semáforo).

[EDIT] IANAL pero ... Si puede uso glibc en su código, a continuación, se puede ver en el código (ya que es LGPL, quien lo utiliza debe permitir tener una copia de la fuente) Por lo tanto, no estoy seguro de haber entendido la situación legal correctamente o tal vez no esté legalmente autorizado a usar glibc en su empresa.

[EDIT2] Después de pensarlo un poco, supongo que esta parte de la ruta de la llamada debe estar protegida por un bloqueo de algún tipo que glibc cree para usted. De lo contrario, usar enganches en código de subprocesos múltiples nunca funcionaría de manera confiable y estoy seguro de que los documentos mencionarían esto. Como el malloc() debe ser seguro para hilos, los ganchos deben estar también.

Si todavía está preocupado, le sugiero que escriba un pequeño programa de prueba con dos hilos que asignan y liberan memoria en un bucle. Incrementa un contador en el gancho. Después de un millón de rondas, el contador debe ser exactamente dos millones. Si esto se cumple, entonces el gancho está protegido por el bloqueo malloc() también.

[EDIT3] Si la prueba falla, entonces, debido a su situación legal, no es posible implementar el monitor. Dile a tu jefe y deja que tome una decisión al respecto.

[EDIT4] googlear convirtió este comentario de un informe de error:

Los ganchos no son seguros para subprocesos. Período. ¿Qué estás tratando de arreglar?

Esto es parte de un debate de marzo de 2009 sobre un error en libc/malloc/malloc.c que contiene una solución. Así que tal vez una versión de glibc después de esta fecha funciona, pero no parece haber una garantía. También parece depender de su versión de GCC.

+0

No tengo permitido mirar el código GPL de mi compañía. Ellos son las reglas. –

+0

Como el código de gancho debe eliminar el código de gancho antes de volver a invocar malloc, un segundo subproceso que invoca malloc mientras que he desenganchado mi gancho no utilizará el gancho. –

+0

@Alex Supongo que eso significa que no puedes mirar o usar el código GPL. –

3

Tengo el mismo problema. Lo he resuelto con ese ejemplo. Si no definimos THREAD_SAFE, tenemos el ejemplo dado por el hombre, y tenemos un error de segmentación. Si definimos THREAD_SAFE, no tenemos ningún error de segmentación.

#include <malloc.h> 
#include <pthread.h> 

#define THREAD_SAFE 
#undef THREAD_SAFE 

/** rqmalloc_hook_ */ 

static void* (*malloc_call)(size_t,const void*); 

static void* rqmalloc_hook_(size_t taille,const void* appel) 
{ 
void* memoire; 

__malloc_hook=malloc_call; 
memoire=malloc(taille);  
#ifndef THREAD_SAFE 
malloc_call=__malloc_hook; 
#endif 
__malloc_hook=rqmalloc_hook_; 
return memoire; 
} 

/** rqfree_hook_ */ 

static void (*free_call)(void*,const void*); 

static void rqfree_hook_(void* memoire,const void* appel) 
{ 
__free_hook=free_call; 
free(memoire);    
#ifndef THREAD_SAFE 
free_call=__free_hook;  
#endif 
__free_hook=rqfree_hook_; 
} 

/** rqrealloc_hook_ */ 

static void* (*realloc_call)(void*,size_t,const void*); 

static void* rqrealloc_hook_(void* memoire,size_t taille,const void* appel) 
{ 
__realloc_hook=realloc_call;  
memoire=realloc(memoire,taille); 
#ifndef THREAD_SAFE 
realloc_call=__realloc_hook;  
#endif 
__realloc_hook=rqrealloc_hook_; 
return memoire; 
} 

/** memory_init */ 

void memory_init(void) 
{ 
    malloc_call = __malloc_hook; 
    __malloc_hook = rqmalloc_hook_; 

    free_call = __free_hook; 
    __free_hook = rqfree_hook_; 

    realloc_call = __realloc_hook; 
    __realloc_hook = rqrealloc_hook_; 
} 

/** f1/f2 */ 

void* f1(void* param) 
{ 
void* m; 
while (1) {m=malloc(100); free(m);} 
} 

void* f2(void* param) 
{ 
void* m; 
while (1) {m=malloc(100); free(m);} 
} 

/** main */ 
int main(int argc, char *argv[]) 
{ 
memory_init(); 
pthread_t t1,t2; 

pthread_create(&t1,NULL,f1,NULL); 
pthread_create(&t1,NULL,f2,NULL); 
sleep(60); 
return(0); 
} 
0

No hay manera de utilizar los ganchos malloc de una manera segura para subprocesos mientras recursiva en malloc. La interfaz está mal diseñada, probablemente irreparable.

Incluso si coloca un mutex en el código de su gancho, el problema es que las llamadas al malloc no ven esas cerraduras hasta después de que pasaron por el mecanismo de gancho, y pasan a través del mecanismo de gancho, observan variables globales (los punteros del gancho) sin adquirir su mutex. Mientras guarda, cambia y restaura estos punteros en un subproceso, las llamadas de asignador en otro subproceso se ven afectadas por ellos.

El problema principal de diseño es que los ganchos son punteros nulos por defecto. Si la interfaz simplemente proporcionaba ganchos predeterminados no nulos que son el asignador adecuado (el asignador de nivel inferior que no llama más ganchos), entonces sería simple y seguro agregar ganchos: solo podría guardar los ganchos anteriores, y en los nuevos ganchos, recurse en malloc llamando a los ganchos de retención, sin tocar ningún puntero global (que no sea el tiempo de instalación del gancho, lo que se puede hacer antes de que se inicie ningún subproceso).

Alternativamente, glibc podría proporcionar una interfaz interna malloc que no invoque a los ganchos.

Otro diseño sensato sería el uso de almacenamiento local de hilos para los ganchos. Anular y restaurar un gancho se haría en un hilo, sin molestar a los ganchos vistos por otro hilo.

Tal como está, lo que puede hacer para usar el gancho Glibc Malloc de forma segura es evitar recurrir a malloc. No cambie los punteros del gancho dentro de las devoluciones de llamada del gancho, y simplemente llame a su propio asignador.

Cuestiones relacionadas