Obligement - L'Amiga au maximum

Dimanche 24 février 2019 - 03:00  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

 · Accueil
 · A Propos
 · Articles
 · Galeries
 · Glossaire
 · Liens
 · Liste jeux Amiga
 · Quizz
 · Téléchargements
 · Trucs et astuces


Articles

 · Actualité (récente)
 · Actualité (archive)
 · Comparatifs
 · Dossiers
 · Entrevues
 · Matériel (tests)
 · Matériel (bidouilles)
 · Points de vue
 · En pratique
 · Programmation
 · Reportages
 · Tests de jeux
 · Tests de logiciels
 · Tests de compilations
 · Articles divers

 · Articles in english
 · Articles en d'autres langues


Twitter

Suivez-nous sur Twitter




Liens

 · Sites de téléchargements
 · Associations
 · Pages Personnelles
 · Matériel
 · Réparateurs
 · Revendeurs
 · Presse et médias
 · Programmation
 · Logiciels
 · Jeux
 · Scène démo
 · Divers


Jeux Amiga

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Trucs et astuces

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Glossaire

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Partenaires

Annuaire Amiga

Amedia Computer

Relec

Hit Parade


Contact

David Brunet

Courriel

 


Dossier : Gestion de la mémoire sur AmigaOS
(Article écrit par Philippe Vautrin et extrait d'Amiga News Tech - septembre 1991)


L'Amiga étant un système multitâche, la gestion de la mémoire par le système est autrement plus compliquée que celle d'autres systèmes plus banals, comme l'IBM PC ou l'Atari ST. Il lui faut en effet notamment gérer les problèmes de fragmentation qui découlent des multiples allocations successives par plusieurs tâches différentes.

Pour savoir à tout instant quelles sont les zones de mémoire utilisées par des applications et celles disponibles ainsi que leur taille, Exec doit maintenir à jour une liste de pointeurs vers ces zones. Quand une application lui demande de la mémoire, la plupart du temps, via AllocMem(), Exec parcourt ces listes à la recherche de la première zone suffisamment grande pour la contenir. C'est d'ailleurs l'une des premières raisons de la fragmentation : même si plus loin dans la liste, se trouvait une zone de mémoire libre d'exactement la même taille que la requête, Exec allouerait tout de même la première zone suffisamment grande.

Une fois cette mémoire trouvée dans la liste, Exec l'en retire, jusqu'à ce qu'un appel à FreeMem() l'autorise à l'y replacer. Naturellement, les applications doivent libérer toute la mémoire qu'elles allouent, et seulement celle-ci. En fait, Exec ne rechignera pas à libérer une zone mémoire allouée par quelqu'un d'autre ; simplement, lorsque son légitime propriétaire essaiera à son tour de la libérer, Exec se rendra compte que cette zone est déjà de retour dans la "free list" et assumera que quelque chose ne tourne plus rond dans la machine... Du coup, plutôt que de laisser des applications entrer en conflit, il provoquera un petit gourou (n°#80000009) pour signifier aux petits rigolos concernés qu'on ne la lui fait pas...

Alternativement, Exec ne garde aucune trace de qui alloue quoi. Cela signifie que si une application "oublie" (sans jeu de mots) de rendre sa mémoire, il n'y aura aucun moyen autre que la réinitialisation, de la récupérer.

Encore une liste

Comme beaucoup (trop ?) de choses dans l'Amiga, la mémoire est gérée à partir d'une liste, dont la base se trouve dans la structure ExecBase. Son nom est MemList, à ne pas confondre avec la structure MemList, définie dans les includes "exec/memory.h" et"execimemory.i". Les noms similaires pourraient facilement vous induire en erreur. Rappel : vous obtenez un pointeur sur la structure ExecBase soit en ouvrant explicitement la bibliothèque exec.library, soit en allant le chercher à l'adresse 4.

La liste de la mémoire libre est organisée comme une "liste de listes". Le champ MemList pointe en fait sur une liste de régions de mémoire, une "région" n'étant finalement rien d'autre qu'une zone de mémoire libre dans laquelle Exec peut aller piocher à sa guise. A l'initialisation du système, une région est d'abord créée pour la mémoire Chip puis pour chaque extension de mémoire connectée (c'est d'ailleurs là l'utilité du programme MergeMem du répertoire System : si deux extensions occupent des adresses contiguës, MergeMem regroupe les deux listes en une seule).

Chaque région est décrite par une structure C baptisée MemHeader et définie, elle aussi, dans "exec/memory.h" et "exec/memory.i" :

Gestion mémoire AmigaOS

Donc, pour obtenir l'adresse de la première région de la liste :

Gestion mémoire AmigaOS

Normalement, nous avons là un pointeur sur la liste de la mémoire Chip. La figure 1 montre cela un peu plus clairement.

Gestion mémoire AmigaOS

A l'intérieur de cette structure MemHeader, le noeud (node) sert à relier entre elles les différentes régions. Le champ ln_Succ pointe sur la prochaine, le champ ln_Pred, sur la précédente.

La structure MemHeader définit l'adresse la plus basse (mh_Lower) et l'adresse la plus haute (mh_Upper) de la région, ainsi que la taille de mémoire disponible dans cette région (mh_Free). C'est ce champ, mh_Free, qui est décrémenté et incrémenté au fur et à mesure des allocations et des libérations de mémoire par les applications. Le MemHeader contient également un pointeur sur le premier bloc mémoire disponible dans cette région (mh_First).

Un bloc mémoire est appelé en terminologie Amiga, un "chunk", et est bien entendu défini par une structure C que l'on trouve, encore une fois, dans "exec/memory.h" et "execimemory.i" :

Gestion mémoire AmigaOS

Un champ mc_Next à NULL indique la fin de la liste des blocs. Toute la mémoire située entre la fin de la région et son dernier chunk est libre.

A l'initialisation du système, chaque région ne contiendra qu'un seul bloc. Dans le MemHeader, mh_First et mh_Lower sont égaux, ainsi que mh_Upper et mh_Free.

La plus petite quantité de mémoire allouable dans une région est de huit octets, car c'est exactement la taille d'un MemChunk (quatre octets pour le pointeur mc_Next et quatre autres pour le mot long mc_Bytes). De toute façon, AllocMem() aussi bien que FreeMem() arrondissent et la taille demandée et l'adresse de la mémoire réservée à leur plus grand multiple de 8. La plus grande quantité est évidemment égale à la taille de cette région.

A la première allocation dans une région donnée, la taille du bloc demandé est additionnée à mh_First et soustraite de mh_Free. Dans le MemChunk, mc_Next reste à NULL et la taille demandée est soustraite de mc_Bytes.

Si une deuxième demande d'allocation pour cette même région survient, la taille du bloc demandé sera à nouveau additionnée à mh_First et soustraite de mh_Free et de mc_Bytes. Il n'y aura toujours qu'un chunk dans la liste, simplement sa taille aura diminué.

Le premier cas de fragmentation de la mémoire survient lorsque le premier bloc est libéré, alors que le second reste alloué. Dans ce cas, mh_First sera d'abord copié dans mc_Next puis remis à sa valeur initiale (égale à mh_Lower) et mh_Free sera augmenté de la taille de ce bloc. Cette taille sera également placée dans mc_Bytes. Arrivés à ce stade, nous aurons :

Pour le MemHeader
  • mh_First pointant sur le MemChunk
  • mh_Free égal à la taille de la région moins la taille du second bloc.
Pour le MemChunk
  • mc_Next pointant sur la fin du second bloc.
  • mc_Bytes égal à la taille du premier bloc.
La figure 2 visualise tout cela de manière un peu plus concise.

Gestion mémoire AmigaOS

Quand le premier bloc sera à son tour libéré (en assumant qu'aucune autre allocation n'ait eu lieu dans cette région), le MemHeader et le MemChunk retrouveront leur configuration originale, et le second MemChunk sera tout simplement oublié.

Les fonctions d'Exec

Exec offre plusieurs paires de routines pour allouer et libérer la mémoire, la plus connue et la plus employée étant bien évidemment la paire AllocMem()/FreeMem(). On précise lors de l'appel à ces fonctions, la taille de mémoire que l'on désire et ses attributs, c'est-à-dire Chip, Fast et/ou Public. La mémoire Chip est celle accessible par les processeurs spécialisés (Denise, Agnus et Paula) et devra être employée pour tout ce qui concerne les images devant être "blittées", le son, les tampons mémoire des disquettes, etc. La mémoire Fast est celle qui n'est pas Chip ; ces deux attributs sont donc contradictoires et s'annulent l'un l'autre. Enfin, la mémoire Public est la mémoire destinée à être partagée par plusieurs tâches, ou bien par une tâche donnée et des interruptions... Elle peut aussi bien être Chip que Fast. Dans la version actuelle d'Exec (jusqu'à la V34 correspondant au Kickstart 1.3), la mémoire Public n'est pas gérée et cet attribut est tout simplement ignoré, mais il est vivement conseillé de le mettre quand besoin est, pour des raisons de compatibilité avec les futures versions du système.

Lorsqu'aucun attribut particulier n'est requis, Exec essaie d'abord d'allouer de la mémoire Fast (sous réserve qu'il y en ait de disponible et en quantité suffisante), sinon de la Chip. Par conséquent, il est quasiment inutile de préciser que l'on désire de la Fast, sauf cas exceptionnel. Si aucune mémoire n'est disponible, un pointeur NULL est renvoyé, laissant à la charge du programme appelant le soin de réagir en conséquence (normalement, sortie "propre" avec avertissement à l'utilisateur).

Notez au passage que si Exec essaie d'abord d'allouer de la mémoire Fast, c'est simplement parce que celle-ci a une priorité plus élevée que la mémoire Chip. Avec un petit programme adéquat, on peut très bien forcer l'inverse.

Il existe également la paire AllocEntry() et FreeEntry(), qui de manière interne, appele AllocMem() et FreeMem(). Ces fonctions permettent d'allouer et de libérer plusieurs blocs de mémoire en un seul appel, grâce à une structure MemEntry définie, encore une fois, dans "exec/memory.h" et "exec/memory.i" :

Gestion mémoire AmigaOS

Rappel : en langage C, une union, à l'inverse d'une structure, ne réserve pas de mémoire pour chacun de ses membres, mais seulement pour le plus long ; ainsi, elle peut prendre à tour de rôle le type de chacun de ses membres. Ici, la structure MemEntry occupe donc 8 octets de mémoire, et non 12 comme on pourrait le penser à première vue.

AllocEntry() et FreeEntry0 utilisent également une seconde structure, connue sous le nom de MemList (encore une fois, ne confondez pas avec le champ MemList de la structure ExecBase ! Les deux n'ont en commun que le nom !). Cette structure est de taille variable, et est définie ainsi :

Gestion mémoire AmigaOS

Le noeud n'est là que pour vous permettre de lier plusieurs MemLists entre elles ; il est totalement ignoré par AllocEntry() et FreeEntry(). Vient ensuite le nombre de structures MemEntry dans cette MemList, puis les MemEntrys elles-mêmes. Cette possibilité n'est que très rarement utilisée sur l'Amiga.

Troisème paire de routines, Allocate() et Deallocate(). Elles agissent de manière similaire à AllocMem() et à FreeMem(), mais permettent de prendre en compte une région de mémoire propre à l'utilisateur. En général, ce sera un gros bloc alloué avec AllocMem(), dans lequel allouera d'autres petits blocs en fonction des besoins. C'est l'idéal, par exemple, pour un interpréteur BASIC. L'un des programmes de démonstration utilise ces deux fonctions pour mettre en évidence le principe de l'allocation de mémoire développé dans cet article. Pour information, sachez tout de même qu'AllocMem() et FreeMem( appellent, de manière interne, ces fonctions, qui sont donc la base de toute la gestion de la mémoire par Exec.

Enfin, on peut citer par soucis d'exhaustivité, d'autres routines d'allocations que l'on trouve dans d'autres bibliothèques qu'Exec. Je pense notamment à AllocRemember() et FreeRemember() d'intuition.library, à AllocRaster() et FreeRaster() de la graphics.library... Ces fonctions spécialisées sont destinées à faciliter l'allocation de certaines mémoires particulières (bitplanes, etc.) et appellent toutes, de façon interne, AllocMem() et FreeMem().


[Retour en haut] / [Retour aux articles]