El asignador de memoria más influyente de la historia cabía en 15 líneas
En noviembre de 1973, la cuarta edición de Unix ya tenía la mayor parte de su kernel escrita en C, y su rutina para reservar memoria ocupaba unas quince líneas. No liberaba nada, no devolvía errores y no obligaba a nadie a comprobar punteros nulos. Un análisis publicado en el blog napcakes desentierra ese código y deja una conclusión incómoda para cualquier equipo de producto: algunas interfaces que damos por buenas son más pobres que las que se escribieron hace medio siglo.
El texto es un ejercicio de arqueología sobre las fuentes originales de Unix, desde la V4 hasta la V10, y el hilo conductor es aburrido y fundamental a la vez: cómo se reservaba y liberaba memoria en C.
¿Cómo asignaba memoria Unix en 1973?
En los fuentes del "compilador" de C de V4 —que en realidad era una cadena de herramientas separadas, no un compilador monolítico— aparece una función alloc() de unas quince líneas. Alinea el tamaño pedido, comprueba si queda espacio en la región y, si no, extiende el heap con sbrk(2000). Si eso falla, escribe "Out of space" y mata el proceso.
¿Y esto cómo se aplica en tu negocio?
Dueños de negocio de Latinoamérica y España usan un agente de IA para ganar más y recuperar su tiempo. El Kit «tu mano derecha con IA» te muestra cómo en un video de 11 minutos, con una ficha para armar el tuyo. CAR está cerrada por ahora; al anotarte, te avisamos cuando abra.
👥 Quiero el Kit →Es un asignador lineal. Y tiene dos propiedades que hoy pagaríamos caro por recuperar: como usa sbrk(), extender el heap no invalida ningún puntero ya entregado; y como nunca libera memoria, quien llama no tiene que comprobar un NULL en cada línea.
La razón de no liberar era simple: todo vivía durante toda la vida del programa. No había nada que liberar.
El malloc() de Dennis Ritchie tenía algo que el actual no tiene
En el directorio de Dennis Ritchie aparece un archivo malloc.c con dos funciones: malloc(mp, size) y mfree(mp, size, aa). La diferencia con las actuales salta a la vista: reciben un argumento extra, mp, un "mapa". En el uso real se pasaban objetos como coremap y swapmap, que en /usr/sys/systm.h se declaran simplemente como regiones planas de memoria.
Es decir: el malloc() original trabajaba sobre regiones explícitas, no sobre un heap global e implícito. Y mfree() recibía el tamaño del objeto que liberaba, como debería.
El autor lo dice sin rodeos: son interfaces "infinitamente mejores" que las de malloc()/free(). Con ese diseño podrías liberar de golpe toda la memoria que le delegaste a una librería descartando la región entera, limitar cuánta memoria reserva código de terceros y contener el daño de un bug de corrupción de heap.
La razón histórica de ese diseño, sin embargo, no era elegancia sino hardware. En el comentario clásico de John Lions sobre la sexta edición de Unix se explica que se gestionaban dos recursos distintos: memoria principal en unidades de 32 palabras (64 bytes) y área de swap en disco en unidades de 256 palabras (512 bytes). Cada recurso tenía su propio mapa, y por eso había que pasarlo explícitamente: así las rutinas no necesitaban saber con qué tipo de recurso trabajaban.
Del rollback al new(): las buenas ideas que se quedaron en el camino
La V6, publicada en la primavera de 1975, es también la primera edición donde aparece la palabra "arena". Y en la que el autor identifica como la primera versión de diff(1) hay un detalle notable: una función reserva memoria y después retrocede la región reasignando el puntero base, algo que hoy llamaríamos rollback de un arena.
En la V7, de enero de 1979, aparece una macro que cualquier desarrollador actual reconocería:
#define ALLOC(x) (struct x *) ckalloc(sizeof(struct x))
Se usa como p = ALLOC(foo). Curiosamente, solo aparece en dos programas: el compilador de Fortran 77 y make, ambos trabajados por Stuart Feldman. La misma edición introduce assert() y amplía notablemente las páginas de manual.
En la V8, de febrero de 1985, la cosa se pone mejor. Aparece una macro tryalloc que, además de reservar, inicializa a cero todo el bloque. Y aparecen definiciones de new(type) que son, literalmente, lo que uno escribiría hoy. El autor confiesa que al verlas "saltó de la silla".
El detalle jugoso: Cfront, el C++ de la época, convivía con esas macros. Pero Bjarne Stroustrup escribió en A History of C++: 1979-1991 que el operador new venía de Simula, no de ahí. Su argumento era de ergonomía: comparaba X* p = new X(2); con el ritual de malloc, comprobación de NULL e inicialización manual. Y añadía una frase que suena a cualquier post-mortem de rendimiento actual: tener programas reales gastando el 50% o más de su tiempo en malloc() no era aceptable.
Simula 67, por cierto, ya tenía objetos, clases, herencia y recolección de basura, y su reporte de 1964 usaba new solo para desambiguar gramática. Stroustrup lo recicló.
¿Por qué desaparecieron las regiones explícitas?
El autor especula con tres razones. La mayoría de los programas tenían vidas de objeto triviales: reservar, leer, procesar, imprimir. La memoria era escasa y no había memoria virtual, así que peleaban por cada bloque libre. Y, sobre todo, se cansaron de gestionar memoria a mano: quitar el mapa de la interfaz simplifica la superficie, aunque haga la implementación más lista.
Hay una prueba de que no era solo comodidad. En la V7 se añadió galloc(3), con recolección de basura, y Rob Pike definió en su editor Sam una macro gcnew que la usaba. La gente más cercana a C quería recolección de basura. Se ve también en lo que hicieron después con Limbo y Go.
Qué significa esto para tu startup
El artículo es historia del software, pero las decisiones que describe son las mismas que tomas cada semana al definir una API interna.
- Pasa el contexto de forma explícita. El
mpde Ritchie es exactamente lo que hoy hace Zig: no haymallocen el lenguaje, y cuando una librería necesita heap recibe un objetoAllocatorque le pasa quien la llama. Según InfoWorld, ese diseño busca evitar asignaciones ocultas y permite que el código cliente elija el asignador adecuado para su entorno. Si tu librería interna decide sola dónde reserva memoria, estás repitiendo el error que Unix corrigió en 1973 y volvió a cometer después. - Agrupa por tiempo de vida, no por tipo de dato. El consejo clásico de P. J. Plauger en The Standard C library (1992) sigue vigente: reúne datos relacionados y asígnalos de una vez, y asigna juntos los objetos que van a morir al mismo tiempo. Eso reduce la fragmentación y, sobre todo, evita el bug de liberar algo que otra parte del sistema todavía usa.
- Documenta el porqué de tus interfaces. El caso más llamativo del texto es el de las macros
new(): existían, funcionaban y se perdieron. Nadie sabía quién las había escrito. Una decisión de diseño sin contexto escrito es una decisión que tu equipo va a revertir sin darse cuenta.
Conclusión
La V4 se creía perdida para siempre hasta que apareció una cinta con sus fuentes, y ese hallazgo es una buena metáfora de lo que cuenta el post: las buenas ideas se pierden si nadie las mira. El malloc() con regiones explícitas, el rollback de arena y las macros new() estuvieron ahí, funcionando, y aun así el ecosistema terminó adoptando una interfaz más pobre y más propensa a errores.
Para un founder la lección es concreta: cuando diseñes la API de tu producto o de tu librería interna, pregúntate qué información necesita quien llama para controlar el recurso, y pásasela. La comodidad de hoy suele ser la deuda técnica de dentro de tres años.
Fuentes
¿Y esto cómo se aplica en tu negocio?
Dueños de negocio de Latinoamérica y España usan un agente de IA para ganar más y recuperar su tiempo. El Kit «tu mano derecha con IA» te muestra cómo en un video de 11 minutos, con una ficha para armar el tuyo. CAR está cerrada por ahora; al anotarte, te avisamos cuando abra.
👥 Quiero el Kit →













