Contexte historique
Le quatrième éditeur d’Unix, publié en novembre 1973, est le premier où le noyau est majoritairement écrit en C. À cette époque, le système tourne sur des PDP‑11 et les contraintes matérielles imposent des solutions d’allocation très simples. Le code source original, retrouvé récemment sur bande magnétique, montre comment les développeurs de Ken Thompson et Dennis Ritchie ont résolu le problème de la gestion de la mémoire sans les abstractions modernes.
Allocation linéaire et sbrk
Dans /usr/c/c20.c, la fonction alloc réalise une allocation linéaire :
alloc(an)
{
register int n;
register char *p;
n = an;
n++;
n =& ~01;
if (lasta+n >= lastr) {
if (sbrk(2000) == -1) {
write(2, "Out of space\n", 14);
exit(1);
}
lastr =+ 2000;
}
p = lasta;
lasta =+ n;
return(p);
}
Le code aligne la taille à un multiple de deux octets, compare la fin de la zone courante (lasta) à la limite (lastr) et, en cas de dépassement, étend le heap avec sbrk(2000). L’appel à exit en cas d’échec élimine la nécessité de vérifier NULL à chaque appel. Cette stratégie garantit que les pointeurs déjà retournés restent valides, car sbrk ne déplace pas le segment de données existant.
Malloc et mfree avec cartes de mémoire
Le répertoire dmr contient malloc.c, où les fonctions malloc et mfree acceptent un premier argument mp représentant une « map ». La structure struct map stocke l’adresse et la taille d’un bloc :
struct map {
char *m_size;
char *m_addr;
};
Les cartes coremap et swapmap sont déclarées comme des tableaux d’entiers (int coremap[CMAPSIZ]; int swapmap[SMAPSIZ];) et servent de gestionnaires de deux ressources distinctes : la mémoire principale (unités de 32 mots, soit 64 octets) et la zone d’échange disque (unités de 256 mots, soit 512 octets). Chaque appel, par exemple p = malloc(coremap, sizeof foo);, indique explicitement la région concernée. L’algorithme sous‑jacent n’est pas linéaire : il maintient une liste triée d’intervalles libres et applique une stratégie « first‑fit », en fusionnant les blocs adjacents lors de la libération. Cette approche, décrite dans le commentaire de John Lions sur l’édition 6, minimise la fragmentation interne tout en conservant une implémentation simple adaptée aux limites de la RAM et du swap du PDP‑11.
Analyse des limites et héritage
Le modèle d’allocation de V4 ne prévoit aucune libération globale ; la mémoire vit pendant toute la durée du processus. Cette décision reflète le fait que les programmes Unix de l’époque étaient courts et que le coût d’un ramasse‑miettes était prohibitif. En revanche, le passage à des cartes explicites dans V6 introduit la libération partielle via mfree, mais conserve la contrainte de connaître la taille du bloc, ce qui évite le stockage d’en‑tête supplémentaire. Le design montre clairement l’influence des contraintes matérielles (taille de la RAM, accès disque) sur l’API : les développeurs ont préféré des interfaces à faible overhead plutôt que la flexibilité offerte par les allocateurs modernes. Aujourd’hui, les concepts d’« arena » et de « region‑based allocation » retrouvent un écho dans les bibliothèques de gestion de mémoire spécialisées, mais l’implémentation originale reste un exemple d’ingénierie pragmatique où la simplicité et la prévisibilité priment sur la généralité.