stringtranslate.com

Asignación de memoria dinámica C

La asignación de memoria dinámica de C se refiere a realizar una gestión manual de la memoria para la asignación de memoria dinámica en el lenguaje de programación C a través de un grupo de funciones en la biblioteca estándar de C , a saber , malloc , realloc , calloc , alineado_alloc y free . [1] [2] [3]

El lenguaje de programación C++ incluye estas funciones; sin embargo, los operadores nuevo y eliminar proporcionan una funcionalidad similar y son recomendados por los autores de ese idioma. [4] Aún así, hay varias situaciones en las que el uso de / no es aplicable, como el código de recolección de basura o el código sensible al rendimiento, y es posible que se requiera una combinación de y ubicación  en lugar del operador de nivel superior .newdeletemallocnewnew

Hay disponibles muchas implementaciones diferentes del mecanismo de asignación de memoria real, utilizado por malloc . Su rendimiento varía tanto en el tiempo de ejecución como en la memoria requerida.

Razón fundamental

El lenguaje de programación C gestiona la memoria de forma estática , automática o dinámica . Las variables de duración estática se asignan en la memoria principal, generalmente junto con el código ejecutable del programa, y ​​persisten durante toda la vida útil del programa; Las variables de duración automática se asignan en la pila y van y vienen a medida que se llaman y regresan funciones. Para las variables de duración estática y de duración automática, el tamaño de la asignación debe ser constante en tiempo de compilación (excepto en el caso de matrices automáticas de longitud variable [5] ). Si el tamaño requerido no se conoce hasta el tiempo de ejecución (por ejemplo, si se leen datos de tamaño arbitrario del usuario o de un archivo de disco), entonces el uso de objetos de datos de tamaño fijo es inadecuado.

La vida útil de la memoria asignada también puede ser motivo de preocupación. Ni la memoria estática ni la de duración automática son adecuadas para todas las situaciones. Los datos asignados automáticamente no pueden persistir en múltiples llamadas a funciones, mientras que los datos estáticos persisten durante la vida del programa, ya sea que sean necesarios o no. En muchas situaciones, el programador requiere una mayor flexibilidad para gestionar la vida útil de la memoria asignada.

Estas limitaciones se evitan mediante el uso de la asignación de memoria dinámica , en la que la memoria se administra de manera más explícita (pero más flexible), generalmente asignándola desde el almacén gratuito (informalmente llamado "montón"), [ cita necesaria ] un área de memoria estructurada para este propósito. En C, la función de biblioteca mallocse utiliza para asignar un bloque de memoria en el montón. El programa accede a este bloque de memoria mediante un puntero que mallocregresa. Cuando la memoria ya no es necesaria, se pasa el puntero freeque desasigna la memoria para que pueda usarse para otros fines.

La descripción original de C indicaba que callocy cfreeestaban en la biblioteca estándar, pero no malloc. Se proporcionó código para una implementación de modelo simple de un administrador de almacenamiento para Unixalloc con y freecomo funciones de la interfaz de usuario, y usando la sbrkllamada al sistema para solicitar memoria del sistema operativo. [6] La documentación de Unix de la sexta edición proporciona allocy freecomo funciones de asignación de memoria de bajo nivel. [7] Las rutinas mallocy freeen su forma moderna se describen completamente en el manual de Unix de la séptima edición. [8] [9]

Algunas plataformas proporcionan bibliotecas o llamadas a funciones intrínsecas que permiten la asignación dinámica en tiempo de ejecución desde la pila de C en lugar del montón (por ejemplo, alloca()[10] ). Esta memoria se libera automáticamente cuando finaliza la función de llamada.

Resumen de funciones

Las funciones de asignación de memoria dinámica de C se definen en stdlib.hel encabezado ( cstdlibencabezado en C++). [1]

Diferencias entre malloc()ycalloc()

Ejemplo de uso

Crear una matriz de diez números enteros con alcance automático es sencillo en C:

matriz int [ 10 ]; 

Sin embargo, el tamaño de la matriz se fija en el momento de la compilación. Si uno desea asignar una matriz similar dinámicamente sin usar una matriz de longitud variable , que no se garantiza que sea compatible con todas las implementaciones de C11 , se puede usar el siguiente código:

int * matriz = malloc ( 10 * tamaño de ( int ));     

Esto calcula la cantidad de bytes que ocupan diez números enteros en la memoria, luego solicita esa cantidad de bytes mallocy asigna el resultado a un puntero denominado array(debido a la sintaxis de C, los punteros y las matrices se pueden usar indistintamente en algunas situaciones).

Debido malloca que es posible que no pueda atender la solicitud, es posible que devuelva un puntero nulo y es una buena práctica de programación verificar esto:

int * matriz = malloc ( 10 * tamaño de ( int )); if ( matriz == NULL ) { fprintf ( stderr , "malloc falló \n " ); devolver -1 ; }             

Cuando el programa ya no necesita la matriz dinámica , eventualmente debe llamar freepara devolver la memoria que ocupa al almacén gratuito:

gratis ( matriz );

La memoria reservada por mallocno se inicializa y puede contener elementos no deseados : los restos de datos previamente utilizados y descartados. Después de la asignación con malloc, los elementos de la matriz son variables no inicializadas . El comando callocdevolverá una asignación que ya se ha borrado:

int * matriz = calloc ( 10 , tamaño de ( int ));    

Con realloc podemos cambiar el tamaño de la cantidad de memoria a la que apunta un puntero. Por ejemplo, si tenemos un puntero que actúa como una matriz de tamaño y queremos cambiarlo a una matriz de tamaño , podemos usar realloc.

int * arr = malloc ( 2 * tamaño de ( int )); arreglo [ 0 ] = 1 ; arreglo [ 1 ] = 2 ; arr = realloc ( arr , 3 * tamaño de ( int )); arreglo [ 2 ] = 3 ;                

Tenga en cuenta que se debe suponer que realloc ha cambiado la dirección base del bloque (es decir, si no ha podido ampliar el tamaño del bloque original y, por lo tanto, ha asignado un nuevo bloque más grande en otro lugar y ha copiado el contenido anterior en él). Por lo tanto, cualquier puntero a direcciones dentro del bloque original tampoco será válido.

Tipo de seguridad

mallocdevuelve un puntero vacío ( void *), que indica que es un puntero a una región de tipo de datos desconocido. El uso de conversión es necesario en C++ debido al fuerte sistema de tipos, mientras que este no es el caso en C. Se puede "convertir" (ver conversión de tipos ) este puntero a un tipo específico:

int * ptr , * ptr2 ; ptr = malloc ( 10 * tamaño de ( * ptr )); /* sin conversión */ ptr2 = ( int * ) malloc ( 10 * sizeof ( * ptr )); /* con un yeso */             

Existen ventajas y desventajas al realizar un yeso de este tipo.

Ventajas del casting

Desventajas del casting

Errores comunes

El uso inadecuado de la asignación de memoria dinámica con frecuencia puede ser una fuente de errores. Estos pueden incluir errores de seguridad o fallas del programa, generalmente debido a fallas de segmentación .

Los errores más comunes son los siguientes: [15]

No comprobar si hay errores de asignación
No se garantiza que la asignación de memoria se realice correctamente y, en su lugar, puede devolver un puntero nulo. El uso del valor devuelto, sin comprobar si la asignación se realizó correctamente, invoca un comportamiento indefinido . Esto generalmente conduce a una falla (debido al error de segmentación resultante en la desreferencia del puntero nulo), pero no hay garantía de que ocurra una falla, por lo que confiar en eso también puede generar problemas.
Pérdidas de memoria
No desasignar memoria usando freeconduce a la acumulación de memoria no reutilizable, que el programa ya no utiliza. Esto desperdicia recursos de memoria y puede provocar errores de asignación cuando estos recursos se agotan.
Errores lógicos
Todas las asignaciones deben seguir el mismo patrón: asignación mediante malloc, uso para almacenar datos, desasignación mediante free. No cumplir con este patrón, como el uso de memoria después de una llamada a free( puntero colgante ) o antes de una llamada a malloc( puntero salvaje ), llamar freedos veces ("doble libre"), etc., generalmente causa una falla de segmentación y resulta en un caída del programa. Estos errores pueden ser transitorios y difíciles de depurar; por ejemplo, la memoria liberada generalmente no es recuperada inmediatamente por el sistema operativo y, por lo tanto, los punteros pendientes pueden persistir por un tiempo y parecer que funcionan.

Además, como interfaz que precede a la estandarización ANSI C, mallocy sus funciones asociadas tienen comportamientos que intencionalmente se dejaron a la implementación para que los definiera por sí mismos. Uno de ellos es la asignación de longitud cero, que es más problemática reallocya que es más común cambiar el tamaño a cero. [16] Aunque tanto POSIX como la Especificación Única de Unix requieren un manejo adecuado de las asignaciones de tamaño 0 mediante devolución NULLo algo más que pueda liberarse de forma segura, [17] no todas las plataformas están obligadas a cumplir con estas reglas. Entre los muchos errores de doble liberación a los que ha dado lugar, el RCE de WhatsApp de 2019 fue especialmente destacado. [18] Una forma de ajustar estas funciones para hacerlas más seguras es simplemente verificar las asignaciones de tamaño 0 y convertirlas en aquellas de tamaño 1. (El retorno NULLtiene sus propios problemas: de lo contrario, indica una falla por falta de memoria. este caso reallochabría indicado que la memoria original no se movió ni se liberó, lo que nuevamente no es el caso para el tamaño 0, lo que lleva a la doble liberación.) [19]

Implementaciones

La implementación de la gestión de la memoria depende en gran medida del sistema operativo y la arquitectura. Algunos sistemas operativos proporcionan un asignador para malloc, mientras que otros proporcionan funciones para controlar ciertas regiones de datos. El mismo asignador de memoria dinámica se utiliza a menudo para implementar tanto mallocel operador como newen C++ . [20]

Basado en montón

La implementación de asignadores heredados se realizaba comúnmente utilizando el segmento de montón . El asignador normalmente expandiría y contraería el montón para cumplir con las solicitudes de asignación.

El método del montón adolece de algunos defectos inherentes:

dlmalloc y ptmalloc

Doug Lea ha desarrollado el dominio público dlmalloc ("Doug Lea's Malloc") como un asignador de propósito general, a partir de 1987. La biblioteca GNU C (glibc) se deriva de ptmalloc ("pthreads malloc") de Wolfram Gloger, una bifurcación de dlmalloc con mejoras relacionadas con el subprocesamiento. [21] [22] [23] A noviembre de 2023, la última versión de dlmalloc es la versión 2.8.6 de agosto de 2012. [24]

dlmalloc es un asignador de etiquetas de límite. La memoria en el montón se asigna como "fragmentos", una estructura de datos alineada de 8 bytes que contiene un encabezado y memoria utilizable. La memoria asignada contiene una sobrecarga de 8 o 16 bytes para el tamaño del fragmento y los indicadores de uso (similar a un vector de droga ). Los fragmentos no asignados también almacenan punteros a otros fragmentos libres en el área de espacio utilizable, lo que hace que el tamaño mínimo del fragmento sea de 16 bytes en sistemas de 32 bits y de 24/32 (depende de la alineación) bytes en sistemas de 64 bits. [22] [24] : 2.8.6, Tamaño mínimo asignado 

La memoria no asignada se agrupa en " contenedores " de tamaños similares, implementados mediante el uso de una lista de fragmentos con doble enlace (con punteros almacenados en el espacio no asignado dentro del fragmento). Los contenedores se clasifican por tamaño en tres clases: [22] [24] : estructuras de datos superpuestas 

El desarrollador de juegos Adrian Stone sostiene que dlmalloc, como asignador de etiquetas de límites, no es amigable para los sistemas de consola que tienen memoria virtual pero no requieren paginación . Esto se debe a que sus devoluciones de llamada ( sysmalloc/ systrim) que reducen y aumentan el grupo no se pueden usar para asignar y confirmar páginas individuales de memoria virtual. En ausencia de localización de la demanda, la fragmentación se convierte en una preocupación mayor. [27]

Jemalloc de FreeBSD y NetBSD

Desde FreeBSD 7.0 y NetBSDmalloc 5.0, la implementación anterior ( phkmallocde Poul-Henning Kamp ) fue reemplazada por jemalloc, escrito por Jason Evans. La razón principal de esto fue la falta de escalabilidad phkmallocen términos de subprocesos múltiples. Para evitar conflictos por bloqueos, jemallocse utilizan "arenas" separadas para cada CPU . Los experimentos que miden el número de asignaciones por segundo en aplicaciones de subprocesos múltiples han demostrado que esto hace que se escale linealmente con el número de subprocesos, mientras que tanto para phkmalloc como para dlmalloc el rendimiento fue inversamente proporcional al número de subprocesos. [28]

malloc de OpenBSD

La implementación de la función por parte de OpenBSDmalloc utiliza mmap . Para solicitudes de tamaño mayor a una página, la asignación completa se recupera usando mmap; Los tamaños más pequeños se asignan desde grupos de memoria mantenidos mallocdentro de una serie de "páginas de depósito", también asignadas con mmap. [29] [ se necesita una mejor fuente ] En una llamada a free, la memoria se libera y se desasigna del espacio de direcciones del proceso usando munmap. Este sistema está diseñado para mejorar la seguridad aprovechando la aleatorización del diseño del espacio de direcciones y las funciones de página de espacio implementadas como parte de mmap la llamada al sistema de OpenBSD , y para detectar errores de uso después de la liberación, ya que una gran asignación de memoria queda completamente desasignada después de liberarse. , su uso posterior provoca un error de segmentación y la finalización del programa.

El proyecto GrapheneOS comenzó inicialmente trasladando el asignador de memoria de OpenBSD a la biblioteca Bionic C de Android. [30]

Acaparar malloc

Hoard es un asignador cuyo objetivo es el rendimiento de asignación de memoria escalable. Al igual que el asignador de OpenBSD, Hoard utiliza mmapexclusivamente, pero gestiona, la memoria en trozos de 64 kilobytes llamados superbloques. El montón de Hoard se divide lógicamente en un único montón global y varios montones por procesador. Además, hay un caché local de subprocesos que puede contener un número limitado de superbloques. Al asignar solo superbloques en el montón local por subproceso o por procesador, y mover superbloques en su mayoría vacíos al montón global para que puedan ser reutilizados por otros procesadores, Hoard mantiene la fragmentación baja mientras logra una escalabilidad casi lineal con la cantidad de subprocesos. . [31]

mimalloc

Un asignador de memoria compacto de uso general y código abierto de Microsoft Research centrado en el rendimiento. [32] La biblioteca tiene aproximadamente 11.000 líneas de código .

Malloc de almacenamiento en caché de subprocesos (tcmalloc)

Cada subproceso tiene un almacenamiento local de subprocesos para asignaciones pequeñas. Para asignaciones grandes se puede utilizar mmap o sbrk . TCMalloc, un malloc desarrollado por Google, [33] tiene recolección de basura para el almacenamiento local de subprocesos muertos. Se considera que TCMalloc es más del doble de rápido que ptmalloc de glibc para programas multiproceso. [34] [35]

En el kernel

Los núcleos del sistema operativo necesitan asignar memoria tal como lo hacen los programas de aplicación. Sin embargo, la implementación mallocdentro de un kernel a menudo difiere significativamente de las implementaciones utilizadas por las bibliotecas C. Por ejemplo, es posible que los buffers de memoria deban cumplir con restricciones especiales impuestas por DMA , o que la función de asignación de memoria se pueda llamar desde el contexto de interrupción. [36] Esto requiere una mallocimplementación estrechamente integrada con el subsistema de memoria virtual del núcleo del sistema operativo.

Anulando malloc

Debido a que mallocy sus parientes pueden tener un fuerte impacto en el rendimiento de un programa, no es raro anular las funciones de una aplicación específica mediante implementaciones personalizadas que estén optimizadas para los patrones de asignación de la aplicación. El estándar C no proporciona ninguna forma de hacer esto, pero los sistemas operativos han encontrado varias formas de hacerlo explotando los enlaces dinámicos. Una forma es simplemente vincular una biblioteca diferente para anular los símbolos. Otro, empleado por Unix System V.3 , es crear punteros de función que una aplicación puede restablecer malloca freefunciones personalizadas. [37]

La forma más común en sistemas tipo POSIX es configurar la variable de entorno LD_PRELOAD con la ruta del asignador, de modo que el vinculador dinámico use esa versión de malloc/calloc/free en lugar de la implementación libc.

Límites de tamaño de asignación

El bloque de memoria más grande posible mallocque se puede asignar depende del sistema host, particularmente del tamaño de la memoria física y de la implementación del sistema operativo.

En teoría, el número más grande debería ser el valor máximo que se puede contener en un size_ttipo, que es un entero sin signo dependiente de la implementación que representa el tamaño de un área de memoria. En el estándar C99 y posteriores, está disponible como SIZE_MAXconstante desde . Aunque ISO C no lo garantiza , suele estarlo .<stdint.h>2^(CHAR_BIT * sizeof(size_t)) - 1

En los sistemas glibc, el bloque de memoria más grande posible mallocque se puede asignar es sólo la mitad de este tamaño, es decir , . [38]2^(CHAR_BIT * sizeof(ptrdiff_t) - 1) - 1

Extensiones y alternativas

Las implementaciones de la biblioteca C que se envían con varios sistemas operativos y compiladores pueden venir con alternativas y extensiones a la mallocinterfaz estándar. Entre estos destaca:

Ver también

Referencias

  1. ^ ab 7.20.3 Funciones de gestión de memoria (PDF) . Especificación ISO/IEC 9899:1999 (Informe técnico). pag. 313.
  2. ^ Cumbre, Steve. "Capítulo 11: Asignación de memoria". C Notas de programación . Consultado el 11 de julio de 2020 .
  3. ^ "aligned_alloc(3) - página de manual de Linux".
  4. ^ Stroustrup, Bjarne (2008). Programación: principios y práctica con C++ . Addison Wesley. pag. 1009.ISBN _ 978-0-321-54372-1.
  5. ^ "manual de gcc". gnu.org . Consultado el 14 de diciembre de 2008 .
  6. ^ Brian W. Kernighan, Dennis M. Ritchie, El lenguaje de programación C , Prentice-Hall, 1978; La Sección 7.9 (página 156) describe callocy cfree, y la Sección 8.7 (página 173) describe una implementación para allocy free.
  7. ^ alloc(3)  -  Manual del programador de Unix versión 6
  8. ^ malloc(3)  -  Manual del programador de Unix versión 7
  9. ^ Anónimo, Manual del programador de Unix, vol. 1 , Holt Rinehart y Winston, 1983 (derechos de autor propiedad de Bell Telephone Laboratories, 1983, 1979); La manpágina para mallocetc. se encuentra en la página 275.
  10. ^ alloca(3)  -  Manual de funciones de la biblioteca FreeBSD
  11. ^ calloc(3)  –  Manual del programador de Linux – Funciones de biblioteca
  12. ^ ab "Casting malloc". Cprogramming.com . Consultado el 9 de marzo de 2007 .
  13. ^ "sonido metálico: archivo fuente lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp". clang.llvm.org . Consultado el 1 de abril de 2018 .
  14. ^ "Lista de preguntas frecuentes de comp.lang.c · Pregunta 7.7b". C-Preguntas frecuentes . Consultado el 9 de marzo de 2007 .
  15. ^ Apesta, Kenneth (4 de agosto de 1997). Consejos sobre C (1 ed.). Pearson. ISBN 9780673999863.
  16. ^ "MEM04-C. Cuidado con las asignaciones de longitud cero - Estándar de codificación SEI CERT C - Confluence". wiki.sei.cmu.edu .
  17. ^ "POSIX.1-2017: malloc". pubs.opengroup.org . Consultado el 29 de noviembre de 2019 .
  18. ^ Despertado (2 de octubre de 2019). "Cómo un error de doble liberación en WhatsApp se convierte en RCE" . Consultado el 29 de noviembre de 2019 .
  19. ^ Felker, rico [@RichFelker] (3 de octubre de 2019). "Vaya. WhatsApp RCE fue el comportamiento incorrecto para realloc (p,0) en el que insisten tantas implementaciones" (Tweet) . Consultado el 6 de agosto de 2022 - vía Twitter .
  20. ^ Alexandrescu, Andrei (2001). "Diseño moderno en C++: programación genérica y patrones de diseño aplicados ". Addison-Wesley. pag. 78.
  21. ^ "Página de inicio de malloc de Wolfram Gloger". malloc.de . Consultado el 1 de abril de 2018 .
  22. ^ abc Kaempf, Michel (2001). "Trucos de Vudo malloc". Phrack (57): 8. Archivado desde el original el 22 de enero de 2009 . Consultado el 29 de abril de 2009 .
  23. ^ "Glibc: componentes internos de Malloc". sourceware.org Trac . Consultado el 1 de diciembre de 2019 .
  24. ^ abc Lee, Doug. "Un asignador de memoria" . Consultado el 1 de diciembre de 2019 .HTTP para código fuente
  25. ^ "Parámetros ajustables de Malloc". GNU . Consultado el 2 de mayo de 2009 .
  26. ^ Sanderson, Bruce (12 de diciembre de 2004). "RAM, memoria virtual, archivo de paginación y todo eso". Ayuda y soporte técnico de Microsoft.
  27. ^ Piedra, Adrián. "El agujero que dlmalloc no puede llenar". Juego Angustia . Consultado el 1 de diciembre de 2019 .
  28. ^ Evans, Jason (16 de abril de 2006). "Una implementación escalable y concurrente de malloc (3) para FreeBSD" (PDF) . Consultado el 18 de marzo de 2012 .
  29. ^ "libc/stdlib/malloc.c". Referencia cruzada de BSD, OpenBSD src/lib/ .
  30. ^ "Historia | GrapheneOS". Grapheneos.org . Consultado el 2 de marzo de 2023 .
  31. ^ Berger, ED; McKinley, KS ; Blumofe, RD; Wilson, PR (noviembre de 2000). Hoard: un asignador de memoria escalable para aplicaciones multiproceso (PDF) . ASPLOS -IX. Actas de la novena conferencia internacional sobre soporte arquitectónico para lenguajes de programación y sistemas operativos . págs. 117-128. CiteSeerX 10.1.1.1.4174 . doi :10.1145/378993.379232. ISBN  1-58113-317-0.
  32. ^ Microsoft lanza malloc() optimizado como código abierto - Slashdot
  33. ^ Página de inicio de TCMalloc
  34. ^ Ghemawat, Sanjay; Ménage, Paul; TCMalloc: Malloc de almacenamiento en caché de subprocesos
  35. ^ Callaghan, Mark (18 de enero de 2009). "MySQL de alta disponibilidad: doble rendimiento de sysbench con TCMalloc". Mysqlha.blogspot.com . Consultado el 18 de septiembre de 2011 .
  36. ^ "kmalloc()/kfree() incluye/linux/slab.h". Gente.netfilter.org . Consultado el 18 de septiembre de 2011 .
  37. ^ Levine, John R. (2000) [octubre de 1999]. "Capítulo 9: Bibliotecas compartidas". Enlazadores y cargadores. La serie Morgan Kaufmann sobre programación e ingeniería de software (1 ed.). San Francisco, Estados Unidos: Morgan Kaufmann . ISBN 1-55860-496-0. OCLC  42413382. Archivado desde el original el 5 de diciembre de 2012 . Consultado el 12 de enero de 2020 .Código: [1][2] Fe de erratas: [3]
  38. ^ "malloc: haga que malloc falle con solicitudes mayores que PTRDIFF_MAX". Fuente Bugzilla . 2019-04-18 . Consultado el 30 de julio de 2020 .
  39. ^ "¿Por qué el uso de alloca() no se considera una buena práctica?". stackoverflow.com . Consultado el 5 de enero de 2016 .
  40. ^ Amarasinghe, Saman; Leiserson, Charles (2010). "6.172 Ingeniería del rendimiento de sistemas de software, Conferencia 10". MIT OpenCourseWare . Instituto de Tecnología de Massachusetts. Archivado desde el original el 22 de junio de 2015 . Consultado el 27 de enero de 2015 .
  41. ^ "alloca(3) - página del manual de Linux". man7.org . Consultado el 5 de enero de 2016 .
  42. ^ posix_memalign  - Referencia de interfaces del sistema, especificación única de UNIX , versión 4 de The Open Group

enlaces externos