Gestion de la mémoire Mac OS classique - Classic Mac OS memory management

"À propos de cet ordinateur" Fenêtre Mac OS 9.1 montrant la consommation de mémoire de chaque application ouverte et le logiciel système lui-même.

Historiquement, le Mac OS classique utilisait une forme de gestion de la mémoire qui est tombée en disgrâce dans les systèmes modernes. La critique de cette approche a été l' un des domaines clés concernés par le changement de Mac OS X .

Le problème initial pour les ingénieurs du Macintosh était de savoir comment utiliser au mieux les 128 Ko de RAM dont la machine était équipée, sur du matériel informatique Motorola 68000 qui ne supportait pas la mémoire virtuelle . Étant donné qu'à cette époque, la machine ne pouvait exécuter qu'un seul programme d' application à la fois et qu'il n'y avait pas de stockage secondaire fixe , les ingénieurs ont mis en œuvre un schéma simple qui fonctionnait bien avec ces contraintes particulières. Ce choix de conception n'a pas bien évolué avec le développement de la machine, créant diverses difficultés pour les programmeurs et les utilisateurs.

Fragmentation

La principale préoccupation des ingénieurs d'origine semble avoir été la fragmentation - c'est-à-dire l'allocation et la désallocation répétées de la mémoire via des pointeurs conduisant à de nombreuses petites zones de mémoire isolées qui ne peuvent pas être utilisées parce qu'elles sont trop petites, même si la mémoire libre totale peut être suffisant pour satisfaire une demande particulière de mémoire. Pour résoudre ce problème, les ingénieurs d' Apple ont utilisé le concept d'une poignée déplaçable , une référence à la mémoire qui permettait de déplacer les données réelles référencées sans invalider la poignée. Le schéma d'Apple était simple - une poignée était simplement un pointeur vers une table (non déplaçable) d'autres pointeurs, qui à son tour pointaient vers les données. Si une demande de mémoire nécessitait un compactage de la mémoire, cela était fait et la table, appelée bloc de pointeur maître, était mise à jour. La machine elle-même a implémenté deux zones de mémoire disponibles pour ce schéma: le tas système (utilisé pour le système d'exploitation) et le tas d'application. Tant qu'une seule application à la fois était exécutée, le système fonctionnait bien. Étant donné que l'ensemble du tas d'application a été dissous lors de la fermeture de l'application, la fragmentation a été minimisée.

Le système de gestion de la mémoire présentait des faiblesses; le tas système n'était pas protégé contre les applications errantes, comme cela aurait été possible si l'architecture du système avait pris en charge la protection de la mémoire , ce qui était souvent la cause de problèmes et de plantages du système. En outre, l'approche basée sur les poignées a également ouvert une source d'erreurs de programmation, où les pointeurs vers des données dans ces blocs déplaçables ne pouvaient pas être garantis pour rester valides à travers les appels qui pourraient provoquer le déplacement de la mémoire. C'était un réel problème pour presque toutes les API système existantes. En raison de la transparence des structures de données appartenant au système à l'époque, les API ne pouvaient pas résoudre ce problème. Il incombait donc au programmeur de ne pas créer de tels pointeurs, ou du moins de les gérer très soigneusement en déréférençant tous les descripteurs après chaque appel d'API. Étant donné que de nombreux programmeurs n'étaient généralement pas familiers avec cette approche, les premiers programmes Mac souffraient fréquemment de défauts résultant de cela.

Palm OS et Windows 16 bits utilisent un schéma similaire pour la gestion de la mémoire, mais les versions Palm et Windows rendent l'erreur du programmeur plus difficile. Par exemple, sous Mac OS, pour convertir une poignée en pointeur, un programme dé-référence simplement la poignée directement, mais si la poignée n'est pas verrouillée, le pointeur peut devenir invalide rapidement. Les appels pour verrouiller et déverrouiller les poignées ne sont pas équilibrés; dix appels à HLock sont annulés par un seul appel à HUnlock . Dans Palm OS et Windows, les poignées sont de type opaque et doivent être dé-référencées MemHandleLock sous Palm OS ou Global/LocalLock sous Windows. Lorsqu'une application Palm ou Windows a terminé avec une poignée, elle appelle MemHandleUnlock ou Global/LocalUnlock . Palm OS et Windows conservent un nombre de verrous pour les blocs; après trois appels à MemHandleLock , un blocage ne sera déverrouillé qu'après trois appels à MemHandleUnlock .

Résoudre le problème des verrous et déverrouillages imbriqués peut être simple (bien que fastidieux) en employant diverses méthodes, mais celles-ci empiètent sur la lisibilité du bloc de code associé et nécessitent une prise de conscience et une discipline de la part du codeur.

Fuites de mémoire et références périmées

La sensibilisation et la discipline sont également nécessaires pour éviter les «fuites» de mémoire (échec de désallocation dans le cadre de l'allocation) et pour éviter les références à des descripteurs périmés après la publication (ce qui entraînait généralement un crash brutal - gênant sur un système à tâche unique, potentiellement désastreux si d'autres programmes sont en cours d'exécution).

Switcher

La situation s'est aggravée avec l'avènement de Switcher , qui était un moyen pour un Mac avec 512 Ko ou plus de mémoire d'exécuter plusieurs applications à la fois. C'était un pas en avant nécessaire pour les utilisateurs, qui trouvaient l'approche une application à la fois très limitative. Parce qu'Apple était désormais attaché à son modèle de gestion de la mémoire, ainsi qu'à la compatibilité avec les applications existantes, il a été contraint d'adopter un schéma dans lequel chaque application se voyait allouer son propre tas à partir de la RAM disponible. La quantité de RAM réelle allouée à chaque tas était définie par une valeur codée dans les métadonnées de chaque application, définie par le programmeur. Parfois, cette valeur n'était pas suffisante pour des types de travail particuliers, donc le paramètre de valeur devait être exposé à l'utilisateur pour lui permettre d'ajuster la taille du tas en fonction de ses propres besoins. Bien que populaire parmi les « utilisateurs expérimentés », cette exposition d'un détail de mise en œuvre technique allait à l'encontre de la philosophie de l'utilisateur Mac. En plus d'exposer les utilisateurs à des aspects techniques ésotériques, c'était inefficace, car une application serait faite pour récupérer toute sa RAM allouée, même si elle en laissait la plus grande partie inutilisée par la suite. Une autre application pourrait manquer de mémoire, mais ne pourrait pas utiliser la mémoire libre «détenue» par une autre application.

Bien qu'une application ne puisse pas utiliser avantageusement le tas d'une application sœur, elle pourrait certainement la détruire, généralement en écrivant par inadvertance à une adresse absurde. Une application traitant accidentellement un fragment de texte ou d'image, ou un emplacement non attribué comme un pointeur, pourrait facilement écraser le code ou les données d'autres applications ou même le système d'exploitation, laissant des "lurkers" même après avoir quitté le programme. Ces problèmes peuvent être extrêmement difficiles à analyser et à corriger.

Switcher a évolué vers MultiFinder dans System 4.2, qui est devenu le Process Manager dans System 7 , et à ce moment-là, le schéma était depuis longtemps enraciné. Apple a fait quelques tentatives pour contourner les limitations évidentes - la mémoire temporaire en était une, où une application pouvait «emprunter» de la RAM libre qui restait en dehors de son tas pendant de courtes périodes, mais cela était impopulaire auprès des programmeurs, ce qui a largement échoué à résoudre les problèmes. L'addon de mise au point du système 7 d'Apple a ajouté une taille de mémoire "minimale" et une taille "préférée" - si la quantité de mémoire préférée n'était pas disponible, le programme pouvait se lancer dans l'espace minimum, éventuellement avec des fonctionnalités réduites. Cela a été incorporé dans le système d'exploitation standard à partir de System 7.1, mais n'a toujours pas résolu le problème de racine.

Les schémas de mémoire virtuelle , qui ont rendu plus de mémoire disponible en paginant des parties de mémoire inutilisées sur le disque, ont été rendus disponibles par des utilitaires tiers tels que Connectix Virtual , puis par Apple dans System 7 . Cela augmentait la capacité de la mémoire Macintosh à un coût de performance, mais n'ajoutait pas de mémoire protégée ou n'empêchait pas le compactage de tas du gestionnaire de mémoire qui invaliderait certains pointeurs.

32 bits propre

À l'origine, le Macintosh disposait de 128 ko de RAM, avec une limite de 512 ko. Ce nombre a été augmenté à 4 Mo lors de l'introduction du Macintosh Plus . Ces ordinateurs Macintosh utilisaient le processeur 68000 , un processeur 32 bits, mais n'avaient que 24 lignes d'adresse physique. Les 24 lignes permettaient au processeur d'adresser jusqu'à 16 Mo de mémoire (2 à 24 octets), ce qui était considéré comme une quantité suffisante à l'époque. La limite de RAM dans la conception Macintosh était de 4 Mo de RAM et 4 Mo de ROM , en raison de la structure de la carte mémoire. Ce problème a été résolu en modifiant la carte mémoire avec le Macintosh II et le Macintosh Portable , permettant jusqu'à 8 Mo de RAM.

La mémoire étant une ressource rare, les auteurs de Mac OS ont décidé de tirer parti de l'octet inutilisé dans chaque adresse. Le gestionnaire de mémoire d'origine (jusqu'à l'avènement du système 7) plaçait des indicateurs dans les 8 bits supérieurs de chaque pointeur et poignée 32 bits . Chaque adresse contenait des indicateurs tels que «verrouillé», «purgeable» ou «ressource», qui étaient stockés dans la table de pointeur maître. Lorsqu'ils étaient utilisés comme adresse réelle, ces indicateurs étaient masqués et ignorés par le CPU.

Bien qu'une bonne utilisation de l'espace RAM très limité, cette conception a posé des problèmes lorsque Apple a présenté le Macintosh II, qui utilisait le processeur 32 bits Motorola 68020 . Le 68020 avait 32 lignes d'adresse physique qui pouvaient adresser jusqu'à 4 Go (2 32 octets) de mémoire. Les indicateurs que le gestionnaire de mémoire stockait dans l'octet de poids fort de chaque pointeur et descripteur étaient désormais significatifs et pouvaient entraîner des erreurs d'adressage.

En théorie, les architectes du logiciel système Macintosh étaient libres de modifier le schéma des «indicateurs dans l'octet haut» pour éviter ce problème, et ils l'ont fait. Par exemple, sur les ordinateurs Macintosh IIci et versions ultérieures, HLock() et d'autres API ont été réécrites pour implémenter le verrouillage des poignées d'une manière autre que de signaler les bits élevés de poignées. Mais de nombreux programmeurs d'applications Macintosh et une grande partie du code logiciel du système Macintosh lui-même ont accédé aux indicateurs directement plutôt que d'utiliser les API, telles que HLock() , qui avaient été fournies pour les manipuler. En faisant cela, ils ont rendu leurs applications incompatibles avec le véritable adressage 32 bits, ce qui est devenu connu comme n'étant pas «32 bits propre».

Afin d'arrêter les plantages continus du système causés par ce problème, System 6 et les versions antérieures fonctionnant sur un 68020 ou un 68030 forceraient la machine en mode 24 bits, et ne reconnaîtraient et traiteraient que les 8 premiers mégaoctets de RAM, une faille évidente dans machines dont le matériel était câblé pour accepter jusqu'à 128 Mo de RAM - et dont la documentation produit annonçait cette capacité. Avec System 7, le logiciel système Mac a finalement été nettoyé 32 bits, mais il y avait toujours le problème des ROM sales. Le problème était que la décision d'utiliser l'adressage 24 bits ou 32 bits doit être prise très tôt dans le processus de démarrage, lorsque les routines ROM ont initialisé le gestionnaire de mémoire pour configurer un environnement Mac de base dans lequel les ROM NuBus et les pilotes de disque sont chargés. et exécuté. Les anciennes ROM ne prenaient pas en charge le gestionnaire de mémoire 32 bits et il n'était donc pas possible de démarrer en mode 32 bits. Étonnamment, la première solution à cette faille a été publiée par la société d'utilitaires logiciels Connectix , dont le produit 1991 MODE32 a réinitialisé le gestionnaire de mémoire et répété les premières parties du processus de démarrage Mac, permettant au système de démarrer en mode 32 bits et permettant l'utilisation de tous la RAM de la machine. Apple a licencié le logiciel de Connectix plus tard en 1991 et l'a distribué gratuitement. Les ordinateurs Macintosh IIci et plus tard Macintosh basés sur Motorola avaient des ROM propres 32 bits.

Il a fallu un certain temps avant que les applications ne soient mises à jour pour supprimer toutes les dépendances 24 bits, et System 7 a fourni un moyen de revenir en mode 24 bits si des incompatibilités d'application étaient détectées. Au moment de la migration vers PowerPC et System 7.1.2, la propreté 32 bits était obligatoire pour créer des applications natives et même plus tard, les Mac basés sur Motorola 68040 ne pouvaient pas prendre en charge le mode 24 bits.

Orientation de l'objet

La montée en puissance des langages orientés objet pour la programmation du Mac - d'abord Object Pascal , puis plus tard C ++ - a également posé des problèmes pour le modèle de mémoire adopté. Dans un premier temps, il semblerait naturel que les objets soient implémentés via des poignées, pour avoir l'avantage d'être déplaçables. Ces langages, tels qu'ils ont été conçus à l'origine, utilisaient des pointeurs pour les objets, ce qui entraînerait des problèmes de fragmentation. Une solution, implémentée par les compilateurs THINK (plus tard Symantec ) , consistait à utiliser des Handles en interne pour les objets, mais à utiliser une syntaxe de pointeur pour y accéder. Cela semblait une bonne idée au début, mais des problèmes profonds sont rapidement apparus, car les programmeurs ne pouvaient pas dire s'ils avaient affaire à un bloc déplaçable ou fixe, et n'avaient donc aucun moyen de savoir s'ils devaient assumer la tâche de verrouiller des objets ou non. Inutile de dire que cela a conduit à un grand nombre de bogues et de problèmes avec ces premières implémentations d'objets. Les compilateurs ultérieurs n'ont pas tenté de le faire, mais ont utilisé de vrais pointeurs, implémentant souvent leurs propres schémas d'allocation de mémoire pour contourner le modèle de mémoire de Mac OS.

Alors que le modèle de mémoire Mac OS, avec tous ses problèmes inhérents, est resté ainsi jusqu'à Mac OS 9 , en raison de graves contraintes de compatibilité des applications, la disponibilité croissante de RAM bon marché signifiait que, dans l'ensemble, la plupart des utilisateurs pouvaient mettre à niveau leur sortie d'un coin. La mémoire n'a pas été utilisée efficacement, mais elle était suffisamment abondante pour que le problème ne devienne jamais critique. Ceci est ironique étant donné que le but de la conception originale était de maximiser l'utilisation de quantités très limitées de mémoire. Mac OS X a finalement supprimé l'ensemble du schéma, implémentant un schéma de mémoire virtuelle moderne et clairsemée . Un sous-ensemble des anciennes API de modèle de mémoire existe toujours pour la compatibilité dans le cadre de Carbon , mais correspond au gestionnaire de mémoire moderne (une malloc implémentation thread-safe ) en dessous. Apple recommande que le code Mac OS X utilise malloc et free «presque exclusivement».

Les références

Liens externes