|
|||||||||||||||||||||||||||||||||||||||||||
|
Voici un examen approfondi du noyau orienté objet du système d'exploitation Amiga L'orientation objet est le mot à la mode en informatique au début des années 1990. C'est le dernier Saint Graal, qui permettra aux programmeurs de sauter d'un seul bond de grands immeubles, de guérir la faim dans le monde et de produire 100 000 lignes de code entièrement déboguées par jour. Enfin, peut-être pas. Après tout, AT'T a inventé l'un des premiers langages de programmation orientés objet, le C++, et n'a pas été en mesure de sortir la version 2.0 moins d'un an après la date de sortie prévue. Néanmoins, il ne fait aucun doute que la programmation orientée objet est une bonne chose, et les producteurs de systèmes d'exploitation ont furieusement recodé leurs produits en tant que systèmes orientés objet, généralement en utilisant le C++. Il existe pourtant un système d'exploitation orienté objet qui est largement utilisé depuis 1985. Il fait fonctionner l'Amiga de Commodore, bien que, ironiquement, il n'ait pas été écrit dans un langage orienté objet. Amiga Exec, une conception "POO" Le système d'exploitation de l'Amiga est parfois incorrectement appelé "AmigaDOS". En fait, le système d'exploitation de l'Amiga a trois composants principaux : Exec (le noyau multitâche), AmigaDOS proprement dit (qui fournit les systèmes de fichiers de haut niveau et l'interface de ligne de commande) et Intuition (la base de l'interface utilisateur graphique). Je ne parlerai que de l'Amiga Exec. Des trois, c'est le plus orienté objet et le plus comparable au langage de programmation C++. Cela peut paraître surprenant. Les interfaces graphiques ne sont-elles pas ce qui a rendu la POO (Programmation Orientée Objet) si célèbre ? Oui, mais la POO ne se limite pas à traiter des objets de données correspondant à des images graphiques. Nous y reviendrons plus tard. Bien que de nombreuses fonctions de l'Amiga aient un nombre potentiellement illimité d'éléments, le minimum absolu de mémoire que le logiciel système de l'Amiga requiert est une fraction de ce que beaucoup de systèmes comparables requièrent ; même en comptant la partie ROM du système d'exploitation, la mémoire consommée est seulement d'environ 512 ko (bien que les Amiga plus récents puissent gérer des ROM plus grandes). Qu'est-ce qui manque donc au système d'exploitation de l'Amiga pour qu'il soit si petit ? Comment l'Amiga parvient-il à fournir des services multitâches et de fenêtrage dans moins de la moitié ou du quart de la mémoire nécessaire aux ordinateurs Apple et IBM ? La réponse réside dans une redondance minimale. Si vous examinez la structure interne de nombreux systèmes d'exploitation populaires, vous découvrirez qu'il s'agit d'un "système d'exploitation contre eux". En d'autres termes, vous avez ce bloc quelque peu monolithique qu'est le système d'exploitation de base, quelques trucs supplémentaires qui servent de pilotes de périphériques et autres, et le code des applications - et seule une trêve malaisée leur permet de se rencontrer. Même si le système d'exploitation peut lui-même être composé de nombreux éléments, son apparence pour le programmeur d'applications reste essentiellement celle d'un édifice assez mystérieux, au-delà duquel seules des portions sélectionnées du logiciel d'application peuvent aller. Le système d'exploitation de l'Amiga est différent. Relativement peu de parties sont totalement opaques ; en fait, avec les révisions ultérieures du système d'exploitation (version 2.0), la tendance a été d'ouvrir davantage ses parties internes pour l'utilisation des applications. Ceci est d'autant plus surprenant que, contrairement au Macintosh, l'Amiga fait tourner ses applications dans un état non privilégié. Pas de magie La plupart des systèmes d'exploitation actuels fonctionnent comme si le code du système d'exploitation était "magique", c'est-à-dire qu'il passe la majeure partie de son temps à fonctionner dans des états privilégiés, à inhiber les interruptions, à exécuter des instructions obscures incompréhensibles pour les simples mortels qui programment des applications, et à faire des choses qui sortent complètement du cadre de la programmation d'applications. Ce n'est pas vraiment vrai. Très peu de code de système d'exploitation est véritablement magique ; la plupart d'entre eux traitent de la gestion de tables, de listes, de files d'attente et d'autres tâches banales de ce type. Cependant, comme il s'agit de tables, de listes, de files d'attente, etc. du système d'exploitation, elles sont généralement gérées par des routines spéciales qui s'exécutent dans des environnements privilégiés, non interruptibles, gérés par la mémoire, ou dans d'autres environnements obscurs. Ce que l'on a tendance à oublier, c'est qu'il est souvent possible de prendre toutes les parties "magiques" du code et de les séparer des parties "non magiques", ce qui permet d'obtenir un ensemble de routines de contrôle pour changer de mode et un grand nombre de routines de traitement des données qui se ressemblent étrangement. Cette situation signifie, d'une part, qu'il est possible de créer des versions polyvalentes de certaines de ces routines pour remplacer toutes ces fonctions similaires mais non identiques et, d'autre part, que ces routines n'étant plus magiques, elles peuvent également être accessibles aux programmes d'application, ce qui réduit leur taille et leur complexité globales, sans parler du temps économisé grâce à l'utilisation d'un code pré-débogué. L'utilisation de code à usage général pour les fonctions critiques du système d'exploitation peut sembler une hérésie pour certains - après tout, les termes "à usage général" et "efficace" ne s'excluent-ils pas mutuellement ? La réponse semble être non, ou plus précisément, "si c'est un code à usage général et qu'il n'est pas efficace, c'est peut-être qu'il n'est pas assez général". Ce qui fait généralement perdre du temps à un code polyvalent, ce sont tous les tests et les branchements qu'il doit effectuer pour gérer les variations des structures de données qu'il traite. Quel est le rôle de la POO dans tout cela ? La réponse réside dans le principe de l'héritage. En organisant les structures de données du système de la même manière que le code système dans les systèmes traditionnels et en plaçant les éléments de données apparentés dans une séquence commune, nous obtenons deux avantages : une réduction de la quantité de traitement des cas spéciaux qui était si choquante et la création d'une hiérarchie de classes d'objets de données. L'effet secondaire est que le système devient plus facile à comprendre : il y a moins de fonctions uniques. Une vue de l'intérieur Exec, comme mentionné plus haut, est le noyau du système d'exploitation de l'Amiga, et il est réalisé de cette manière. Exec consiste en une collection de classes d'objets de plus en plus complexes, comme le montre la figure 1. Lorsqu'une nouvelle classe d'objets Exec est définie sur la base d'une classe plus simple, elle contient tous les objets de données de la classe plus simple et est (généralement) valable non seulement pour les opérations définies pour cette classe, mais aussi pour toutes les opérations relatives à la classe plus simple. C'est ce qu'on appelle l'héritage de fonctions. ![]() Figure 1 : la hiérarchie des objets d'Exec. Le noyau du système d'exploitation de l'Amiga est constitué d'une collection de classes d'objets de plus en plus complexes. Il existe de nombreuses façons de représenter des collections de données en interne, chacune ayant ses propres avantages et inconvénients. Exec est basé sur la liste doublement liée. Les éléments de la liste sont alloués dynamiquement à partir de n'importe quel endroit de la mémoire qui convient (voir le paragraphe "Gestion de la mémoire" plus bas) ; il n'y a donc pas de tables à remplir. En revanche, la vitesse d'accès dépend fortement du nombre de noeuds de la liste, mais il existe des moyens de réduire ce problème, comme je l'expliquerai plus loin. Le système d'exploitation Amiga se distingue par le fait qu'il gère lui-même les listes doublement liées. Exec gère deux niveaux de listes : les listes et les "MinLists" (l'implémentation Lattice C++ en ajoute deux autres qui sont similaires aux listes Exec standard, mais sans initialisation automatique). Celles-ci sont utilisées pour définir les éléments que le logiciel du système Amiga initialise. La MinList est l'ancre d'une liste doublement liée de MinNodes. Les MinNodes contiennent les pointeurs du MinNode suivant et du MinNode précédent. La structure MinList contient une paire de MinNodes fictifs pour simplifier le traitement en réduisant la logique des cas spéciaux nécessaires pour traiter les éléments à la fin de la liste. Une MinList vide se compose toujours de deux MinNodes, les noeuds fictifs situés à l'avant et à la fin de la MinList qui sont tous deux contenus dans la structure de données MinList elle-même. Le pointeur de noeud suivant du noeud fictif avant pointe sur le premier noeud réel de la liste. Son pointeur de noeud précédent est toujours NULL. Il en va de même pour le noeud final fictif. Étant donné que le pointeur de noeud précédent du noeud frontal fictif est toujours NULL et que le pointeur de noeud suivant du noeud final fictif est également toujours NULL, il a été possible d'économiser une petite quantité de mémoire en les faisant se chevaucher en partageant le même pointeur NULL. Les MinNodes fictifs ajoutent une complication : le dernier élément réel de la liste n'est pas celui dont le pointeur du noeud suivant est NULL : cet honneur revient au noeud fictif. Il existe un ensemble complet de fonctions permettant d'insérer et de supprimer des MinNodes aux extrémités ou aux points intermédiaires d'une MinList. En utilisant les fonctions appropriées, une MinList peut donc être utilisée comme premier entré/premier sorti (FIFO) (également connue sous le nom de file d'attente) ou comme dernier entré/premier sorti (LIFO) (également connue sous le nom de pile), ainsi que comme une liste à usage général. Une fois de plus, notez qu'il n'y a rien de magique dans les MinLists, les MinNodes ou les fonctions qui agissent sur eux. Bien qu'elles soient largement utilisées par le système d'exploitation Amiga, elles peuvent être utilisées tout aussi librement dans n'importe quelle application. Amis et membres Une note sur les fonctions amis et membres en C++. Lorsqu'une classe est définie en C++, ses composants internes sont (sauf indication contraire) protégés contre tout accès occasionnel. Il s'agit là d'un argument de vente important : il est plus difficile de corrompre les entrailles d'un objet et plus facile de localiser la fonction responsable. Pour utiliser concrètement (et modifier) les informations contenues dans un objet de classe, il est donc nécessaire de disposer d'un mécanisme d'accès. Le C++ en propose deux : une fonction amie, qui ressemble à une fonction C traditionnelle, sauf qu'en ayant été déclarée amie d'une ou plusieurs classes, elle est autorisée à accéder directement aux données stockées dans cette ou ces classes ; et une fonction membre, qui appartient en fait à une classe spécifique et à laquelle on passe donc un paramètre supplémentaire "invisible" : "this", qui est un pointeur sur l'objet de la classe sur lequel on agit. Exec a été conçu pour être utilisé par des langages non-POO ; ainsi, les fonctions Exec sont, en fait, des fonctions amies. Les fichiers #include créés par MTS Associates pour gérer le C++ sur Amiga les définissent généralement comme telles. Cependant, pour mieux gérer Exec en tant que système orienté objet, un certain nombre de fonctions membres ont également été définies. Par exemple, pratiquement tous les objets de l'Amiga sont dans une sorte de liste, donc la plupart des objets ont une fonction membre nommée "next()". Peu importe ce que c'est, peu importe comment c'est lié, et peu importe le nom ou la position relative du pointeur de l'élément suivant de l'objet, vous êtes donc toujours assuré que vous pouvez obtenir un pointeur sur le suivant dans la liste en utilisant la fonction next(). Listes : MinListes et autres Une liste est une MinList étendue, composée de noeuds. Les noeuds sont des MinNodes plus un champ de type de 1 octet, une priorité de 1 octet et un pointeur sur le nom du noeud, qui est une chaîne au format C. La figure 2 montre la structure d'un noeud. Un noeud incorpore la structure d'un MinNode et hérite donc automatiquement des propriétés d'une MinList pour former une liste. ![]() Figure 2 : les noeuds peuvent être localisés par leur nom et classés par ordre de priorité. Les noeuds sont ancrés par des listes. Comme un noeud incorpore un MinNode, il hérite de toutes les propriétés et fonctions des listes. Signaux Les signaux sont représentés par un mot de 32 bits contenant un ensemble d'indicateurs de signaux. L'utilisateur peut en allouer 16 et le système d'exploitation en réserve 16. Lorsqu'une tâche envoie un signal à une autre tâche et que cette dernière est dans un état d'attente de signal, l'information de signal entrant de la tâche réceptrice comporte les bits de signal entrant logiquement "OU". Ces bits sont ensuite combinés par "ET" avec le modèle de signaux de la tâche destinataire qui est en attente. Si le résultat n'est pas nul, la tâche devient distribuable. C'est un moyen extrêmement efficace d'activer une tâche endormie, et cela peut être fait à n'importe quel niveau du système, y compris dans les routines d'interruption, qui se voient refuser la plupart des services les plus sophistiqués du système. Les messages La figure 3 montre comment un message est construit à partir d'un noeud. Les messages sont extrêmement importants dans le fonctionnement de l'Amiga. Ils sont utilisés pour transmettre des informations d'une tâche à l'autre, comme base pour les requêtes d'entrées/sorties, et comme moyen de transfert pour les événements de souris et de fenêtre d'Intuition. Contrairement à un signal, qui peut simplement donner une indication "Je suis là !", un message peut contenir des informations complexes. ![]() Figure 3 : le système d'exploitation Amiga utilise des "messages" pour transmettre des informations d'une tâche à l'autre, comme base pour les demandes d'entrées/sorties, et comme moyen de transfert pour les événements de souris et de fenêtre d'Intuition. Un message incorpore un noeud et hérite donc de toutes ses propriétés et fonctions. ![]() Figure 4 : un message est transmis à un port de message, qui contient une liste de messages entrants à traiter, par ordre de priorité. Comme les messages, les ports de messages intègrent un noeud et héritent donc de toutes les propriétés et fonctions de ce dernier. crux StdPort *listener=newStdPort("Je t'entends"); Le constructeur StdPort prend en charge tous les détails de l'initialisation standard de MsgPort. La mémoire est allouée et initialisée, et un signal est acquis sur lequel la tâche d'écoute peut attendre. En utilisant la fonction AddPort, le MsgPort peut être placé sur la liste publique MsgPort du système, où il peut être trouvé par n'importe quelle tâche qui souhaite lui envoyer un message. Exec ayant été conçu selon une approche orientée objet, les nouvelles fonctions du système d'exploitation sont assez simples. Une reconstruction en C++ montre ce qui suit : crux void AddPort ( MsgPort *mport ) { Forbid() ;// disable task-switching Enqueue ( AbsExecBase->PortList , mport ) ; Enable () ;// re-enable task-switching } Voici une reconstitution d'une autre fonction du système : crux MsgPort * FindPort ( const char *portname ) { return ( MsgPort *) AbsExecBase->PortList.find ( portname ); } FindPort illustre une autre caractéristique importante de la conception. Si vous recherchiez une liste système chaque fois que vous souhaitez accéder à un élément de cette liste, les performances du système en pâtiraient. Au lieu de cela, la convention est de rechercher et de retourner l'adresse de l'objet. Par la suite, l'adresse de l'objet peut être utilisée directement (l'Amiga n'utilise pas de "handles" (gestionnaires) à la Macintosh, qui font se déplacer les objets en mémoire). L'inconvénient est que vous ne devez jamais déplacer ou supprimer un objet que d'autres tâches peuvent utiliser. Les bibliothèques et les périphériques s'en assurent en maintenant un compteur d'utilisateurs. Pour les ports de messages simples, l'application doit soit imposer une fonction de connexion/déconnexion, soit exiger que tous les messages soient envoyés en une seule fois (c'est-à-dire FindPort/ PutMsg). Chaque tâche est limitée à un maximum de 32 signaux distincts mais peut avoir un nombre illimité de MsgPorts. Le même signal peut être utilisé par plus d'un MsgPort, ce qui permet aux tâches Intuition de ne pas être limitées à un nombre fini de fenêtres ouvertes. IORequests Les IORequests sont des messages étendus qui comprennent des informations de contrôle et de transfert d'entrées/sorties envoyées aux périphériques. Un ensemble de commandes de base (lecture, écriture, contrôle, etc.) est commun à tous les IORequests ; pour un périphérique donné, des extensions supplémentaires peuvent être ajoutées si nécessaire. Un certain nombre de classes IORequest spéciales pour les périphériques ont été dérivées ; tout implémenteur de périphérique est libre de dériver ses propres extensions si nécessaire. Il est assez courant de se retrouver avec quelque chose comme ce qui suit : crux MyDeviceRequest est basé sur : StdlORequest est basé sur : IORequest est basé sur : Message est basé sur : Node est basé sur : MinNode À chaque niveau d'héritage, vous obtenez des propriétés et des fonctions supplémentaires. Le seul nouveau code nécessaire est celui qui gère votre propre classe d'objet. Bibliothèques La bibliothèque est un autre type de noeud important. Elle se compose d'une structure de base, précédée de vecteurs de fonctions et suivie d'un stockage privé facultatif. Il existe un ensemble de fonctions de base (par exemple, ouvrir, fermer et supprimer) communes à toutes les bibliothèques. Au-delà, le concepteur est libre d'ajouter des fonctionnalités à sa guise. Contrairement à la plupart des systèmes d'exploitation, le système d'exploitation Amiga n'utilise pas d'interruptions logicielles ou de trappes d'instructions illégales pour fournir des services de système d'exploitation. Au lieu de cela, il y a une bibliothèque principale, nommée "exec.library", située dans le noyau en ROM. Toutes les fonctions fondamentales du système - les primitives de liste, la gestion de la mémoire, les fonctions de chargement et d'ouverture des bibliothèques (les routines internes d'initialisation et d'ouverture des bibliothèques sont appelées à partir de cette bibliothèque) - sont définies ici. La seule partie immuable du système d'exploitation est l'emplacement 4 de la mémoire absolue, qui pointe vers la structure de la bibliothèque Exec (ExecBase). La partie "données" d'ExecBase contient les structures Exec fondamentales, notamment les définitions de liste pour les ports de message du système, les bibliothèques, les périphériques et les tâches. Il est intéressant de comparer les bibliothèques Exec avec les bibliothèques de liens dynamiques utilisées par OS/2 et Microsoft Windows. Les DLL gèrent des ensembles de fonctions, mais elles fournissent également des services supplémentaires. Les puces Intel 286 et suivantes gèrent les concepts de différents niveaux (anneaux) de sécurité. Si vous ne possédez pas le niveau de sécurité minimum requis, une requête échouera. L'envoi de fonctions DLL peut entraîner un changement de niveau de sécurité. L'équivalent pour la série Motorola 68000 est la fonction d'appel de module. Il nécessite cependant au moins un microprocesseur 68020 et, de préférence, une unité de gestion de la mémoire paginée. AmigaDOS fonctionne sur tous les 68000, donc les seuls niveaux de sécurité intrinsèquement disponibles sont dus au fait que les programmes AmigaDOS s'exécutent dans l'état utilisateur par défaut, alors que le système d'exploitation s'exécute dans l'état superviseur, selon les besoins. Les deux approches présentent des avantages et des inconvénients. Étant donné que les bibliothèques Exec sont essentiellement de simples tables vectorielles, le surcoût lié à l'appel des fonctions de la bibliothèque est à peine plus élevé que lorsque la fonction réside dans le programme appelant (beaucoup moins qu'une interruption logicielle), au lieu d'être un code système partagé. D'autre part, une DLL soigneusement conçue, tout en subissant une légère pénalité en termes de vitesse, est plus à l'abri des dommages causés par des programmes qui se sont emballés. Il convient de noter que les DLL sont des extensions des systèmes d'exploitation Microsoft ; les fonctions de base du système sont toujours gérées par des interruptions logicielles. Il doit donc y avoir une logique pour les deux types d'interfaces de bibliothèques. Les bibliothèques Amiga, cependant, ne fournissent pas seulement une interface unique, mais elles sont immunisées contre le problème inhérent à toutes les interruptions logicielles : il n'y en a qu'un nombre fini, ce qui ne semble jamais être suffisant pour des raisons pratiques. Les bibliothèques, en revanche, sont non seulement extensibles "à l'infini", mais il est facile d'en créer de nouvelles qui ne se distinguent pas des bibliothèques intégrées, voire de remplacer complètement une bibliothèque intégrée en insérant une nouvelle bibliothèque du même nom à un niveau de priorité plus élevé dans la liste des bibliothèques du système. Le concept de bibliothèque est lui-même étendu ; il constitue la base d'un périphérique d'entrées/sorties en ajoutant quelques fonctions standard. La plupart des périphériques fonctionnent par l'intermédiaire de messages étendus, appelés "IORequests". Il existe également des extensions de ces messages (telles que StdlORequest), ainsi que des extensions personnalisées pour des périphériques spécifiques. En règle générale, les périphériques possèdent également une ou plusieurs tâches afin que les entrées/sorties puissent être effectuées de manière asynchrone, bien que cela ne soit pas obligatoire. Il existe une autre extension de la bibliothèque, moins bien comprise, appelée "ressource". Une ressource agit essentiellement comme un coordinateur pour les ressources partagées (généralement matérielles), telles que les différents lecteurs d'un contrôleur de disquette, ou les ports d'entrées/sorties et parallèle (qui sont implémentés sur les mêmes puces). Tâches La structure des tâches est un autre noeud. Celui-ci contient toutes les définitions nécessaires pour faire d'Exec un système d'exploitation pleinement fonctionnel, multitâche, préemptif et axé sur les priorités. Une tâche est à peu près équivalente à un "thread" d'OS/2. Une tâche étendue, connue sous le nom de "processus", fournit des informations supplémentaires pour permettre l'utilisation des fonctions AmigaDOS définies dans la bibliothèque nommée "dos.library", principalement des services d'entrées/sorties de type Unix, des capacités de chargement de programmes, et autres. Le planificateur de tâches d'Exec n'est pas aussi élaboré que celui d'OS/2, dont on dit qu'il a été repris intégralement du système d'exploitation de l'ordinateur central VM/370 d'IBM. Le répartiteur d'OS/2 ajuste dynamiquement les priorités des tâches sur la base de certains algorithmes qui se trouvent dans la partie "magique" du système d'exploitation. Bien que cela soit impressionnant, il est douteux qu'un système d'exploitation mono-utilisateur en ait besoin. Peu importe, vous êtes coincé avec ce système. Le mieux que vous puissiez faire est de le désactiver, mais il consomme toujours de la mémoire réelle. Exec offre de bonnes performances avec un algorithme simple de répartition des tranches de temps. Des algorithmes personnalisés plus complexes peuvent être attachés de manière directe, si nécessaire. Ceci peut être fait en toute sécurité (et de manière indépendante de la version) sur Amiga, parce que les fonctions de distribution et les listes de tâches sont accessibles via des interfaces bien définies. Rien dans la conception de base d'Exec ne nécessite la présence d'un seul processeur dans le système. Exec pourrait être implémenté dans un système multiprocesseur si l'accès aux listes du système était correctement sérialisé. Les ordinateurs Amiga 2500 contiennent un 68000 et un 68020 (ou 68030). Actuellement, l'un ou l'autre est mis en veille au démarrage, mais il y a des possibilités. Sémaphores À l'origine, la sérialisation dans Exec était assurée soit par la fonction Forbid(), qui empêche d'autres tâches d'être exécutées, soit par Disable(), qui désactive les interruptions. Cependant, cela sérialise l'ensemble du système. Pour sérialiser l'accès à une ressource spécifique, les sémaphores sont préférables. Il existe deux types de sémaphores dans Exec. Un sémaphore "SignalSemaphore" est basé sur un MinNode. Il permet une sérialisation très performante, mais son utilisation est soumise à des restrictions. Un sémaphore est basé sur le système de messages et peut être utilisé dans des situations plus générales. Les deux types de sémaphore sont préférables aux fonctions plus grossières Forbid()/Permit() ou Disable()/Enable(), qui réduisent toutes deux la quantité de tâches multiples pouvant être effectuées pendant la sérialisation de la ressource. Interruptions Une interruption est une structure de données qui pointe vers le code de gestion des interruptions, ainsi que vers toute mémoire de travail qu'elle pourrait nécessiter. Pour permettre à plusieurs tâches de gérer une interruption, les interruptions sont des noeuds. La gestion exacte des interruptions varie en fonction du type d'interruption. Gestion de la mémoire Le système d'exploitation de l'Amiga est inhabituel en ce sens qu'il ne partitionne pas la mémoire pour les applications, ou même pour le système d'exploitation lui-même. Au lieu de cela, il maintient une liste de mémoire libre où chaque morceau de mémoire libre a certains attributs, et les demandes sont comparées à ces attributs. Ainsi, aucune application ne manque de mémoire avant que le système lui-même n'en manque, et il n'est pas nécessaire de jongler avec les segments ou, comme avec le Macintosh, de compacter la mémoire. Un ensemble de fonctions de bas niveau peut être utilisé pour acquérir et libérer de la mémoire, mais Exec fournit également un ensemble de fonctions pour gérer la mémoire à l'intérieur des groupes ("pools") acquis par l'application. Cela présente plusieurs avantages : moins de fragmentation globale de la mémoire, moins de charge processeur (puisque l'ensemble du groupe peut être libéré en une seule fois, au lieu d'être libéré au coup par coup), et la possibilité de préallouer suffisamment de mémoire pour les applications qui utilisent beaucoup de mémoire de façon dynamique. Il n'y a pas de liste de mémoire système utilisée. Si une application échoue et n'a pas de routine de nettoyage, ou si le programmeur néglige de libérer toute la mémoire acquise, celle-ci est perdue jusqu'au redémarrage du système. La gestion de la mémoire d'Exec prend en charge à la fois la mémoire commutée par banque et la mémoire virtuelle. La mémoire allouée avec l'attribut "MEMF_PUBLIC" est garantie d'être toujours visible par toutes les tâches, les routines d'interruption et le superviseur du système. La mémoire sur la pile de l'application ou allouée sans cet attribut n'a pas cette garantie. Avec un minimum de 8,5 Mo de mémoire, ni la mémoire virtuelle ni les cartes mémoire commutées ne sont largement utilisées, il est donc probable que de nombreuses applications échoueront si cette situation change. Tous les autres composants du système d'exploitation de l'Amiga - AmigaDOS, Intuition, Workbench, les routines d'animation intégrées uniques du système, etc. - dépendent tous en fin de compte des services d'Exec. Exec est compact, efficace, flexible, fiable et extensible. Et aucun autre système avec lequel j'ai travaillé n'a été aussi facile à utiliser. C'est ce que j'aime. Bibliographie
|