El malloc() perdido de Unix: cuando la memoria se gestionaba con regiones explícitas
The lost malloc()
Un recorrido arqueológico por el código fuente de Unix, desde la cuarta edición de 1973 hasta la octava de 1985, revela cómo Dennis Ritchie y Ken Thompson gestionaban la memoria en C. El malloc() original recibía un mapa explícito (coremap o swapmap) y mfree() exigía el tamaño del objeto, interfaces que el autor considera muy superiores a las actuales. También aparecen asignadores lineales, macros tipo new y la influencia de Simula en el operador new de C++.
Esas son interfaces infinitamente mejores que el malloc()/free() tradicional. Imagina usar una biblioteca con una interfaz así: podrías liberar todos sus recursos de memoria simplemente descartando la región entera que le delegaste.
- adrian_b
El autor no descubrió realmente de dónde había tomado SIMULA 67 el "new".
En noviembre de 1965, C.A.R. Hoare publicó un artículo extremadamente influyente: "Record Handling". Esta era una propuesta de extensión para el sucesor de ALGOL 60.
Casi todas las características que se añadieron en SIMULA 67 respecto a su predecesor, el lenguaje SIMULA, provienen directamente del artículo de Hoare, con dos grandes excepciones: la herencia de clases y los procedimientos "virtuales", que fueron inventados por los creadores de SIMULA 67 (Kristen Nygaard y Ole-Johan Dahl) y que son esenciales para la programación orientada a objetos.
Las palabras clave "class" y "new" de SIMULA 67 se tomaron del artículo de Hoare (en realidad, Hoare había tomado "class" de COBOL 60, que utilizaba una terminología muy diferente en comparación con ALGOL 60, entre las muchas diferencias estaba el uso de "class" en lugar del "type" que usaba ALGOL 60).
El artículo de Hoare discutía muchas características de lenguajes de programación, la mayoría de las cuales eran nuevas, incluyendo la definición de tipos estructurados con constructores y destructores, uniones discriminadas (tomadas de John McCarthy), enumeraciones, el puntero NULL y muchas otras.
SIMULA 67 fue uno de los primeros lenguajes que implementó muchas de las características propuestas por Hoare. Otros lenguajes que implementaron algunas de las características propuestas fueron IBM PL/I, ALGOL 68, y luego un par de años después, en 1970-11, Pascal.
La palabra clave "new" ya había sido utilizada por primera vez por Niklaus Wirth, pero en Wirth tenía un significado diferente que en Hoare. Declaraba una variable […]
- OskarS
> Es más, mfree() recibe el tamaño del objeto que está liberando, como debería.
Siempre he pensado que ese es uno de los mayores pequeños errores en la historia de esta industria. Significa, más o menos, que estás forzando al asignador de memoria a llevar un registro de esos tamaños cuando no debería tener que hacerlo. Prácticamente no hay ningún caso sensato en el que quien llama a free() no sepa el tamaño de la asignación, así que ahora estamos manteniendo ese registro dos veces, en el "espacio de usuario" y dentro del asignador de memoria. Piensa, por ejemplo, en los casos en los que llamarías a malloc:
* Para asignar un único objeto de tipo T, el tamaño es simplemente `sizeof(T)`, así que puedes pasárselo directamente a free().
* Para asignar un array de objetos, el tamaño es `count * sizeof(T)`, y no puedo pensar en una sola vez en la que NO sepas cuál es `count`. O tiene un tamaño estático (en cuyo caso es solo una constante), o si tienes un array que crece dinámicamente, necesitas llevar un registro del espacio reservado para saber cuándo reasignar. Como mínimo, lo necesitas para la comprobación de límites.
* Para bloques de memoria en bruto, también se supone que sabes, al liberar, cuál es el tamaño de la memoria en bruto que asignaste, por las mismas razones que en el punto anterior. Vamos, tener un puntero char* en bruto por ahí sin información de tamaño es simplemente una práctica de C terrible.
Si realmente no sabes el tamaño de tu asignación (muy difícil de imaginar cuándo ocurriría esto), deberías estar obligado, del lado del usuario, a llevar un registro de alguna manera, no delegar […]
- veltas
No me identifico mucho con el autor aquí porque no estoy entusiasmado con los 'arena allocators' y realmente no me importa que free no reciba un tamaño. Creo que la interfaz estaba bien.
Además, los 'arena allocators' (esto significaba algo diferente hace como 20 años, así que creo que el nombre simplemente fue rebautizado) no son tan buenos, hacen algunas cosas más fáciles y a veces mejoran el rendimiento, pero también son propensos a usar más memoria y causar más fragmentación si no se usan con cuidado. Esa es una de las muchas razones por las que evolucionamos lentamente alejándonos de los antiguos asignadores lineales, pero también porque simplemente queríamos una interfaz simple y flexible para ocultar la complejidad.
- fjfaase
La llamada al sistema sbrk en Linux todavía está disponible para obtener más heap del sistema operativo. Así que la memoria todavía se asigna de forma lineal y nunca se libera de nuevo. Quizás ahora funciones como mmap cambian esto. Usé sbrk para implementar malloc para una implementación de biblioteca estándar muy limitada.
- jdw64
Cuando era estudiante universitario (hace 15 años), me encontré por primera vez con C a través de A Book on C, y C parece un lenguaje que es simple pero que realmente puede hacer mucho. Mirar cuán viejo es este lenguaje, y cuántos conceptos ya usados en lenguajes modernos se probaron durante su historia de desarrollo, es sorprendente. Es sorprendente que los conceptos que pensé que eran técnicas modernas hoy ya se estuvieran probando hace décadas, es decir, antes de que yo naciera.
Es increíble que un método como el Bump Allocator ya se hubiera probado.
¿Cómo pudieron los programadores veteranos hacer todo esto? Simplemente me asombra.