¿Por qué no es necesario que múltiples hilos compartan un bloqueo para llamar a mmap, como lo hacen con malloc/calloc/sbrk?
Estoy trabajando con ptmalloc y algo interesante que me encontré es que cuando una arena se queda sin fragmentos disponibles (y el fragmento superior no es lo suficientemente grande), tiene que extender la arena usando sbrk()
o asignar una región no contigua usando mmap()
. Lo que me llamó la atención en particular es que, para asignar más memoria usando sbrk()
, se debe adquirir un bloqueo antes de poder llamar a esta función (además del bloqueo obtenido previamente para tener posesión exclusiva de la arena actual). Sin embargo, no es necesario adquirir ningún bloqueo antes de llamar a mmap()
. A continuación, se incluyen las partes específicas de la función sys_alloc()
del archivo malloc.c incluido en la implementación de ptmalloc (para referencia):
- Llamada para extender la arena usando
sbrk()
:
if (HAVE_MORECORE && tbase == CMFAIL) { // Intentar MORECORE no contiguo
size_t asize = granularity_align(nb + TOP_FOOT_SIZE + SIZE_T_ONE);
if (asize < HALF_MAX_SIZE_T) {
char* br = CMFAIL;
char* end = CMFAIL;
ACQUIRE_MORECORE_LOCK(); // BLOQUEO
br = (char*)(CALL_MORECORE(asize));
end = (char*)(CALL_MORECORE(0));
RELEASE_MORECORE_LOCK(); // DESBLOQUEO
if (br != CMFAIL && end != CMFAIL && br < end) {
size_t ssize = end - br;
if (ssize > nb + TOP_FOOT_SIZE) {
tbase = br;
tsize = ssize;
}
}
}
}
- Llamada para extender la arena usando
mmap()
:
if (HAVE_MMAP && tbase == CMFAIL) { // Intentar MMAP
size_t req = nb + TOP_FOOT_SIZE + SIZE_T_ONE;
size_t rsize = granularity_align(req);
if (rsize > nb) { // Fallar si envuelve alrededor de cero
char* mp = (char*)(CALL_MMAP(rsize));
if (mp != CMFAIL) {
tbase = mp;
tsize = rsize;
mmap_flag = IS_MMAPPED_BIT;
}
}
}
Cualquier ayuda para entender por qué esto puede funcionar incluso con múltiples hilos que tienen el mismo patrón de memoria (y, por lo tanto, tienen que extender sus arenas al mismo tiempo) sin tener que usar bloqueos (es decir, cómo mmap()
garantiza devolver direcciones distintas, incluso si se llama simultáneamente con una dirección sugerida NULL) sería muy apreciada.
davy.ai
Explicación de la asignación de memoria en ptmalloc
En la implementación de
ptmalloc
, cuando un área se queda sin espacio disponible, debe extender el área utilizandosbrk()
o asignar una región no contigua utilizandommap()
. Lo interesante es que, al extender el área utilizandosbrk()
, la implementación adquiere un bloqueo antes de llamarlo, pero no es necesario adquirir un bloqueo antes de llamar ammap()
.Esto se debe a que la asignación realizada utilizando
sbrk()
se realiza en memoria contigua, lo que significa que el nuevo espacio se agrega al espacio existente. Para evitar condiciones de carrera donde varios hilos intentan extender el mismo espacio al mismo tiempo, se adquiere un bloqueo para asegurarse de que solo un hilo esté modificando el montón en un momento dado.Por otro lado,
mmap()
asigna memoria desde el espacio de direcciones virtuales del sistema operativo, que no es contigua con el espacio existente. Por lo tanto, se garantiza que múltiples llamadas simultáneas ammap()
desde diferentes hilos devuelvan direcciones distintas. Por lo tanto, no se requiere un bloqueo para asignar regiones no contiguas utilizandommap()
.Esto significa que en un entorno de múltiples hilos,
mmap()
es una forma más eficiente de asignar memoria, ya que se puede hacer de forma concurrente sin causar contención en un bloqueo.