linux - puddletag - musicbrainz picard portable
¿Malloc crea perezosamente las páginas de respaldo para una asignación en Linux(y otras plataformas)? (6)
Di esta respuesta a una publicación similar sobre el mismo tema:
¿Algunos de los asignadores son flojos?
Esto comienza un poco fuera del tema (y luego lo relacionaré con tu pregunta), pero lo que está sucediendo es similar a lo que sucede cuando bifurcas un proceso en Linux. Al bifurcar hay un mecanismo llamado copiar sobre escritura que solo copia el espacio de memoria para el nuevo proceso cuando la memoria también está escrita. De esta forma, si el ejecutor del proceso bifurcado es un nuevo programa de inmediato, habrá ahorrado la sobrecarga de copiar la memoria original de los programas.
Volviendo a tu pregunta, la idea es similar. Como han señalado otros, al solicitar la memoria obtiene el espacio de la memoria virtual de inmediato, pero las páginas reales solo se asignan cuando se les escribe.
¿Cuál es el propósito de esto? Básicamente hace que la memoria mallocing sea una operación de tiempo más o menos constante Big O (1) en lugar de una operación Big O (n) (similar a la forma en que se distribuye el programador de Linux en lugar de hacerlo en una gran parte).
Para demostrar lo que quiero decir, hice el siguiente experimento:
rbarnes@rbarnes-desktop:~/test_code$ time ./bigmalloc
real 0m0.005s
user 0m0.000s
sys 0m0.004s
rbarnes@rbarnes-desktop:~/test_code$ time ./deadbeef
real 0m0.558s
user 0m0.000s
sys 0m0.492s
rbarnes@rbarnes-desktop:~/test_code$ time ./justwrites
real 0m0.006s
user 0m0.000s
sys 0m0.008s
El programa Bigmalloc asigna 20 millones de entradas, pero no hace nada con ellos. deadbeef escribe una int en cada página, lo que da como resultado 19531 escrituras y justwrites asigna 19531 ints y las elimina. Como puede ver, deadbeef tarda aproximadamente 100 veces más en ejecutarse que bigmalloc y unas 50 veces más que Justwrite.
#include <stdlib.h>
int main(int argc, char **argv) {
int *big = malloc(sizeof(int)*20000000); // Allocate 80 million bytes
return 0;
}
.
#include <stdlib.h>
int main(int argc, char **argv) {
int *big = malloc(sizeof(int)*20000000); // Allocate 80 million bytes
// Immediately write to each page to simulate an all-at-once allocation
// assuming 4k page size on a 32-bit machine.
for (int* end = big + 20000000; big < end; big += 1024)
*big = 0xDEADBEEF;
return 0;
}
.
#include <stdlib.h>
int main(int argc, char **argv) {
int *big = calloc(sizeof(int), 19531); // Number of writes
return 0;
}
En Linux si tuviera malloc(1024 * 1024 * 1024)
, ¿qué hace realmente malloc?
Estoy seguro de que asigna una dirección virtual a la asignación (recorriendo la lista gratuita y creando una nueva asignación si es necesario), pero, ¿realmente crea 1 GiB de páginas de intercambio? ¿O mprotect
el rango de direcciones y crea las páginas cuando las tocas como lo hace mmap
?
(Estoy especificando Linux porque el estándar no dice nada sobre este tipo de detalles, pero me gustaría saber qué hacen otras plataformas también).
En Windows, las páginas están comprometidas (es decir, la memoria gratuita disponible disminuye), pero no se asignarán hasta que toque las páginas (ya sea de lectura o escritura).
Linux difiere la asignación de páginas, también conocido como. ''asignación de memoria optimista''. La memoria que recupera de malloc no está respaldada por nada y cuando la toca puede obtener una condición OOM (si no hay espacio de intercambio para la página que solicita), en cuyo caso un proceso se cancela sin ceremonias .
Ver por ejemplo http://www.linuxdevcenter.com/pub/a/linux/2006/11/30/linux-out-of-memory.html
Malloc asigna memoria fuera de bloques gestionados por libc. Cuando se necesita memoria adicional, la biblioteca va al kernel usando la llamada al sistema brk.
El kernel asigna páginas de memoria virtual al proceso de llamada. Las páginas se administran como parte de los recursos que pertenecen al proceso. Las páginas físicas no se asignan cuando la memoria está borrada. Cuando el proceso accede a cualquier ubicación de memoria en una de las páginas brk''d se produce un error de página. El kernel valida que la memoria virtual ha sido asignada y procede a asignar una página física a la página virtual.
La asignación de páginas no se limita a las escrituras y es bastante distinta de la copia en escritura. Cualquier acceso, lectura o escritura, da como resultado un error de página y el mapeo de una página física.
Tenga en cuenta que la memoria de la pila se asigna automáticamente. Es decir, no se requiere un brk explícito para asignar páginas a la memoria virtual que usa la pila.
9. La memoria (parte de El kernel de Linux , Algunas observaciones sobre el Kernel de Linux por Andries Brouwer) es un buen documento.
Contiene los siguientes programas que demuestran el manejo de Linux de la memoria física frente a la memoria real y explica las funciones internas del kernel.
Normalmente, el primer programa de demostración obtendrá una gran cantidad de memoria antes de que malloc () devuelva NULL. El segundo programa de demostración obtendrá una cantidad mucho menor de memoria, ahora que se utiliza realmente la memoria obtenida anteriormente. El tercer programa obtendrá la misma gran cantidad que el primer programa, y luego se eliminará cuando quiera usar su memoria.
Programa de demostración 1: asigna memoria sin usarla.
#include <stdio.h>
#include <stdlib.h>
int main (void) {
int n = 0;
while (1) {
if (malloc(1<<20) == NULL) {
printf("malloc failure after %d MiB/n", n);
return 0;
}
printf ("got %d MiB/n", ++n);
}
}
Programa de demostración 2: asignar memoria y realmente tocarlo todo.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main (void) {
int n = 0;
char *p;
while (1) {
if ((p = malloc(1<<20)) == NULL) {
printf("malloc failure after %d MiB/n", n);
return 0;
}
memset (p, 0, (1<<20));
printf ("got %d MiB/n", ++n);
}
}
Programa de demostración 3: primero asigne y use luego.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define N 10000
int main (void) {
int i, n = 0;
char *pp[N];
for (n = 0; n < N; n++) {
pp[n] = malloc(1<<20);
if (pp[n] == NULL)
break;
}
printf("malloc failure after %d MiB/n", n);
for (i = 0; i < n; i++) {
memset (pp[i], 0, (1<<20));
printf("%d/n", i+1);
}
return 0;
}
(En un sistema que funciona bien, como Solaris , los tres programas de demostración obtienen la misma cantidad de memoria y no se cuelgan, pero ven que malloc () devuelve NULL).