Obligement - L'Amiga au maximum

Jeudi 28 mars 2024 - 11:06  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

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

Articles in english


Réseaux sociaux

Suivez-nous sur X




Liste des 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,
ALL


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


Galeries

Menu des galeries

BD d'Amiga Spécial
Caricatures Dudai
Caricatures Jet d'ail
Diagrammes de Jay Miner
Images insolites
Fin de jeux (de A à E)
Fin de Jeux (de F à O)
Fin de jeux (de P à Z)
Galerie de Mike Dafunk
Logos d'Obligement
Pubs pour matériels
Systèmes d'exploitation
Trombinoscope Alchimie 7
Vidéos


Téléchargement

Documents
Jeux
Logiciels
Magazines
Divers


Liens

Associations
Jeux
Logiciels
Matériel
Magazines et médias
Pages personnelles
Réparateurs
Revendeurs
Scène démo
Sites de téléchargement
Divers


Partenaires

Annuaire Amiga

Amedia Computer

Relec


A Propos

A propos d'Obligement

A Propos


Contact

David Brunet

Courriel

 


Dossier : AmigaOS et Exec
(Article écrit par Fabrice Neyret et extrait d'Amiga News - février 1992)


C'est du système d'exploitation de notre belle machine que nous allons parler aujourd'hui. La lecture de cet article suppose un minimum de connaissances en informatique : il serait par exemple pénible de devoir expliquer OS/2 et Unix au moment où l'on souhaite faire une comparaison. Un aspect qui peut poser problème est le recours à de fréquents anglicismes. Il faut savoir que certains termes n'ont pas une traduction correcte, et que ce sont les termes anglais que l'on retrouve dans toutes les documentations : il serait donc plutôt déroutant qu'ils ne coïncidassent pas...

Le système d'exploitation de l'Amiga se nomme AmigaOS. La conception d'AmigaOS remonte au début des années 1980, du temps où il s'appelait Tripos. Son nom fut souvent indiqué sous le terme "AmigaDOS" par le passé mais c'est incorrect : AmigaDOS n'est qu'une partie d'AmigaOS qui se charge notamment des opérations sur disque. Le terme "AmigaDOS" fut malgré tout utilisé dès le début de l'histoire de l'Amiga, y compris par ses concepteurs. Voir par exemple le Manuel d'AmigaDOS de Commodore-Amiga.

AmigaDOS

Ainsi, AmigaOS contient essentiellement le noyau multitâche Exec, le DOS proprement dit (système de fichiers, CLI), l'interface graphique Intuition et les "handlers" du matériel (graphique, timers (chronomètres), ports parallèle et série...).

Le noyau Exec

Nous allons nous intéresser ici au noyau Exec sur lequel repose tout le système, d'autant plus intéressant que c'est l'un des rares système/noyau Orienté Objet, ce qui lui confère ouverture, compacité, fiabilité et lisibilité. La mémoire minimale (ROM+RAM) dont le système a besoin pour fonctionner correspond en effet à la moitié voire au quart de celle d'un IBM/OS2 ou d'un Mac, ceci grâce à la redondance minimum permise par l'approche OO.

Dans les autres machines on a en général un système monolithique (qui tourne la plupart du temps en mode privilégié toutes interruptions bloquées), des pilotes (gestion des périphériques) puis des applications. Il faut savoir pourtant que l'essentiel du travail du système consiste à gérer des tables, des listes, des queues, ce qui est reproduit à plusieurs niveaux dans le système ainsi que dans les pilotes de périphériques, voire dans certaines applications.

Dans l'approche OO, on pourra séparer les fonctions réellement intrinsèques au système et les fonctions d'usage général publiques qui peuvent opérer sur plusieurs types d'objets et sont même disponibles pour l'utilisateur (ce qui n'est pas incompatible avec performance, ce sont les mauvaises habitudes qui font juger hérétique le fait qu'une même fonction puisse servir au noyau et aux applications). Ainsi, les mêmes routines gèrent les tâches, les entrées/sorties (I/O), les messages, qui sont avant tout des listes, l'usage de routines spécialisées étant limité au minimum indispensable.

Une routine d'usage général en environnement classique peut se noyer dans la combinaison des particularités, tandis qu'en OO les objets dérivés d'objets plus simples et plus généraux héritent des fonctions qui s'appliquaient à ces derniers. Les structures de données prendront simplement des extensions successives, un peu comme les paquets de données dans les réseaux sous la norme OSI dont les protocoles s'encapsulent au niveau de chaque couche (sauf qu'il n'y aura pas ici l'empilement de fonctions servant à l'évaluer à chaque niveau : chaque couche dispose de ses fonctions mais accède directement à la donnée sans devoir la faire traduire par toutes les couches inférieures !).

Les principaux objets utilisés par Exec

Voici la hiérarchie des principaux objets utilisés par Exec, que nous allons maintenant détailler :

Exec

Exec est basé sur les listes double-chaînées plutôt que les tables et dispose donc de toutes les routines gérant les listes (insertion, destruction, accès, recherche...), ces dernières pouvant être utilisées en FIFO (queue) ou en LIFO (pile) selon la façon dont on range ou on prend les éléments.

A la base, le MinNode est un couple de pointeurs destinés à désigner les MinNodes suivant et précédant dans une MinList, et qui constitue ainsi la liste double-chaînée élémentaire. La structure MinList est donc une poignée sur une liste de MinNodes (éléments de liste, sans contenu), et se compose en fait de deux MinNodes bidons qui correspondront aux extrémités de cette liste, afin d'éviter tout cas particulier.

Notons que ces deux MinNodes extrémités se chevauchent de manière à partager le même pointeur Null : la structure MinList contient alors trois adresses, qui sont le dernier Node, Null (qui est à la fois le noeud suivant le dernier Node et le noeud précédant le premier Node), et l'adresse du premier Node.

Exec
Les MinNodes

Un Node dérive d'un MinNode et contient en plus une priorité (par exemple pour ordonner la liste), un numéro de type, et un nom (au format C: pointeur sur une chaîne terminant par 0). Ces Nodes héritant des propriétés des MinNodes forment des Lists qui dérivent des MinLists et se gèrent donc avec les mêmes routines.

Exec
Un Node

Ces Nodes sont particularisés en beaucoup d'objets, comme les Messages qui incluent une adresse pour la réponse (ReplyPort) et une longueur de message suivie du message lui-même, et les MsgPorts où l'on trouve un pointeur sur la tâche propriétaire du port, le masque des signaux (voir plus bas), des drapeaux (flags) et la liste des messages reçus (par ordre de priorité et d'arrivée).

Les messages

Comme on le devine d'après la présence de certains objets, AmigaOS fonctionne essentiellement par envoi de messages. Pour ce faire, on dispose de deux médias essentiels, les signaux et les messages.

Une tâche peut envoyer des signaux à une ou plusieurs autres, une tâche réceptrice peut les tester ou se mettre en attente de signaux en précisant dans un masque ceux qui sont habilités à la réveiller (elle quitte alors la liste des tâches candidates au processeur jusqu'à son réveil). Seize canaux de signal sont réservés pour le système et seize pour les utilisateurs, Exec attribue à la demande les canaux libres (mais leur signification est locale à l'échange entre deux tâches et le même signal peut être utilisé plusieurs fois entre des "groupes de communications" disjoints).

Ce mécanisme est très efficace pour activer des tâches, il est utilisé à tous les niveaux (par exemple pour les routines d'interruption).

Les messages sont utilisés quand il faut échanger un contenu (tandis que les signaux servent plutôt à la synchronisation, c'est-à-dire à donner le "top"), par exemple pour les requêtes d'entrée-sortie ou pour les événements liés aux fenêtres et à la souris et signalés à la demande par Intuition (voir chapitre suivant). L'implémentation des fonctions d'entrée/sortie de type DOS ou Unix-C de haut niveau repose réellement sur ces envois de sages à bas niveau, ce qui les rend particulièrement performantes dans la mesure où seuls les pointeurs circulent et sont recyclés, sans recopie effective de caractères entre les diverses couches logicielles.

Un message est envoyé à l'un des ports de messages (MsgPort) de la tâche visée, le nombre de ces ports étant illimité contrairement aux signaux : les messages s'appuient sur les signaux, mais peuvent utiliser les mêmes. Une tâche qui se serait mise en attente (pour libérer du temps machine) d'un signal partagé devrait alors à son réveil consulter les diverses "boîtes aux lettres" utilisant cette alarme pour savoir où lire un message. Cela dit, rares sont les tâches qui ont besoin de plus de seize boîtes aux lettres, si ce n'est des serveurs de communication comme l'interface graphique, ou un système de partage de ressources...

Les Ports de messages peuvent être privés (et anonymes) ou publics (et nommés) c'est-à-dire compris dans la liste générale des MsgPorts. Les boîtes aux lettres publiques permettent les services du type annuaire : on peut dialoguer avec une tâche avec la seule connaissance de son nom "officiel" en clair, et vérifier ainsi sa présence, ou encore on peut s'adresser à un service en mentionnant son appellation, qui peut être implémenté par la tâche qui sied au propriétaire de la machine (impression, messagerie...). C'est le même principe que les interfaces de connexions d'Unix-Berkeley ("sockets"), en plus simple et en plus efficace dans la mesure où c'est à la base du système dans l'Amiga.

On utilise en général un port de chaque sorte, le public servant à recevoir (un MsgPort étant une simple liste de messages, il faut bien séparer les messages en attente de lecture des messages écrits en instance de départ) et le privé à répondre (ne serait-ce que pour accuser réception et recycler le message, qu'Exec peut se charger de libérer).

Mémoire publique

Cette façon de transmettre et recycler des blocs de mémoire est économique, souple et rapide, mais peut poser des problèmes de sécurité : cela suppose qu'une tâche accède à la zone de mémoire d'une autre tâche (celle qui a initialisé le bloc), ce qui impose un minimum de rigueur dans la réutilisation ou la libération de ces blocs.

Comme on le verra plus loin, l'Amiga reconnaît un type de mémoire "publique", qui devrait être utilisée en pareil cas, mais qui ne correspond pas pour l'instant à une implémentation particulière. On aurait pu également concevoir une fonction d'allocation de mémoire spécifique aux messages, le système se chargeant de libérer le bloc quand plus personne ne s'en sert.

ARexx

Le langage de communication entre tâches REXX, que l'on trouve notamment sur de gros systèmes, avait naturellement sa place dans ce contexte et a été porté sur Amiga (sous le nom d'ARexx). Les nombreuses applications qui le reconnaissent s'équipent de deux ports de messages supplémentaires permettant de recevoir des instructions de l'extérieur ou de retourner des résultats.

On peut alors très simplement faire communiquer des applications, comme un éditeur et un compilateur, ou un tableur et un traitement de texte (c'est d'autant plus facile que le standard de données IFF décrit pratiquement tout ce qui existe). Ces applications doivent être pilotables par mots-clés et macros, et une utilisation élémentaire d'ARexx consiste à définir dans une première application une macro qui va envoyer des ordres à une autre : un compilateur peut alors appeler un éditeur et lui demander de charger le fichier qu'il compilait et de se positionner à la ligne erronée, ou à contrario un éditeur peut appeler un compilateur et récupérer le numéro de ligne et le message d'erreur et se positionner de lui-même.

Un interpréteur permet de superviser les enchaînements plus complexes, soit en exécutant un petit script, soit en déroulant une véritable application ARexx. Ce langage n'est pas sans rappeler le BASIC (le fait qu'il soit interprété n'est pas bien grave : il est là pour coordonner, pas pour exécuter les routines de calcul !), avec en plus la reconnaissance de structures dont les champs sont établis dynamiquement (c'est de la mémoire associative), de puissantes fonctions de "parsing" (pour analyser des lignes de commande), l'adoption du jeu d'instructions et des variables de base définies par la tâche avec laquelle on est en train de communiquer, etc.

Une panoplie de routines

Afin de faciliter la gestion de tous ces objets et parce qu'il en a lui-même besoin, le système offre toutes une panoplie de routines d'allocation et d'initialisation, de manipulations comme la recherche (par exemple d'un port nommé) et bien sûr de communication. L'utilisation de pointeurs est beaucoup moins lourde que les Handles du Mac, avec l'inconvénient correspondant : il faut éviter de détruire ou déplacer un objet public ! Pour cela, les bibliothèques et les périphériques logiques utilisent des compteurs lors des ouvertures et fermetures, afin de ne libérer un objet que si plus personne ne s'en sert.

Les IORequests dérivent des Messages et sont systématiquement utilisées pour transmettre (ou recevoir) des informations aux périphériques (devices). Les requêtes sont classiquement des écritures, des lectures (synchrones, ou asynchrones avec accusé de terminaison) ou du contrôle, mais on utilise en fait un objet dérivé plus riche, le StdIORequest, lui-même généralement particularisé selon le type de périphérique logique, ce qui ne gêne en rien puisque l'on hérite de toute façon de toutes les fonctions des classes plus générales. On utilise donc le plus souvent la suite d'inclusions MinNode/Node/Message/IORequest/StdIORequest/MyDevRequest.

Bibliothèques dynamiques

Un autre aspect fondamental d'AmigaOS réside dans l'emploi systématique de bibliothèques (libraries) dynamiques partagées, qui peuvent rappeler les DLL (liens dynamiques) d'OS/2 ou les "shared libraries" annexes d'Unix. A la différence de la plupart des autres systèmes, l'Amiga n'utilise pas d'interruption logicielle ou de Trap Instruction illégale pour servir les fonctions système : il existe une bibliothèque-maître (exec.library) dans le noyau en ROM qui contient les fonctions système fondamentales (primitives pour la gestion des listes et de la mémoire, ouverture et fermeture de bibliothèques...) et la seule valeur immuable du système est l'adresse $4 qui pointe sur la base de la bibliothèque Exec. Une "Library" contient une structure de base précédée d'un vecteur de fonctions et suivie d'une éventuelle zone de stockage privée.

Le passage par une table de fonctions est bien sûr un peu plus long que si la routine était directement incluse dans le code de l'application (quelle économie de mémoire !), mais moins que si l'on utilisait une interruption logicielle (qui lance tout un processus de sauvegarde de contexte et de détermination de la routine à appeler, et dont le nombre est en général dramatiquement fini ce qui nécessite l'utilisation de registres pour préciser la fonction visée).

Par rapport à cette dernière solution, les bibliothèques sont infiniment extensibles et à chaque nouveau périphérique ou outil est souvent adjoint un "mytool.library". On peut même aisément personnaliser ou mettre à jour une bibliothèque système en utilisant le même nom avec une priorité supérieure (correspondant à un numéro de version). D'autre part, les bibliothèques non stratégiques sont chargées ou vidées de la mémoire en fonction de leur utilisation (un répertoire "libs" à la racine du disque système les rassemble).

Un intérêt évident tient dans ce qu'une application peut fonctionner sans recompilation quel que soit le processeur et sans préjuger de la présence d'un coprocesseur tout en utilisant automatiquement la puissance disponible, dans la mesure où seules les bibliothèques système seront changées lors de l'extension de la machine.

Les Resources sont une classe dérivée des Libraries, et permettent de coordonner l'usage qui est fait des diverses ressources d'un même circuit (par exemple). C'est indispensable sur une machine remplie de composants spécifiques comme l'Amiga si l'on veut rendre compatible le multitâche avec l'utilisation du matériel qui n'a pas le don d'ubiquité, lui...

Le reste est plus commun aux machines multitâches :

Tasks

Une Task (tâche) est un Node auquel on ajoute toutes les informations de priorité et de contexte nécessaires, le Process est une extension au niveau du DOS qui permet d'utiliser la dos.library en conservant un contexte supplémentaire pour le service des I/O (liste et caractéristiques des fichiers ouverts, comme sous Unix).

Le Sheduler (ordonnanceur) se base sur un simple temps partagé (on donne la parole à une autre tâche au bout d'un laps de temps, en fonction de sa priorité) ce qui est largement suffisant (mais le système est suffisamment ouvert pour que l'on puisse modifier cet aspect sans trop de problèmes). C'est moins complexe que pour OS/2 qui calcule des priorités dynamiques (on le soupçonne d'être fortement inspiré de l'IBM VM/370) : c'est lourd, cher et pas vraiment utile sur un système mono-utilisateur, ce qui fait qu'en général on le débranche pour économiser du temps...

On remarque qu'il n'y a rien de spécifiquement mono-processeur dans Exec (il suffirait de sérialiser avec un Semaphore les accès aux listes du système pour garantir qu'un seul processeur les modifie en même temps), ce qui ouvre des perspectives intéressantes dans la mesure où la plupart des cartes processeur ont leur propre mémoire et laissent le 68000 en place (mais il faudrait découpler les bus)...

Semaphores

Les Semaphores dérivent des messages ou des signaux (SignalSemaphore) et permettent une gestion des sections critiques plus respectueuses du multitâche que l'emploi de "Forbid" et "Permit" qui le désactive ! (le principe consiste à s'assurer qu'on est le seul à faire une opération délicate en réservant une sorte de ressource, convenue à l'avance pour cette opération : si quelqu'un d'autre l'a réservée et ne l'a pas encore libérée, il faut attendre).

Interrupts

Une Interrupt est un Node suivi d'un pointeur sur le code à appeler (plus un éventuel bloc de mémoire), le Node permettant d'établir une structure en liste et donc de chaîner par ordre de priorité une série de routines à appeler lors de l'interruption.

Typiquement, on peut avec facilité intervenir aux diverses étapes du traitement d'informations en provenance du clavier ou de la souris simplement en adaptant la priorité. Comme chaque routine transmet à la suivante le message traité, on peut filtrer de façon totalement transparente et par exemple retourner le sens de la souris ou introduire des raccourcis clavier, tout le système se conformant à la nouvelle configuration sans problèmes...

Gestion de la mémoire

Le "Memory Management" (gestion de la mémoire) est un peu inhabituel par rapport aux autres systèmes multitâches : il n'y a pas de partitionnement de la mémoire entre les applications (et le système) mais des listes de blocs (chunk) et de mémoire libre, ce qui fait que le "out of memory" n'intervient que quand toute la mémoire est effectivement consommée. Cela évite aussi d'être tenté de translater les objets en mémoire (comme sur Mac).

Mais cela pose un problème évident de sécurité : toute tâche qui, mal écrite ou prise de folie, se mettrait à déborder de sa zone risque de perturber le fonctionnement d'autres tâches, voire du système si ce dernier ne se rend pas compte assez vite de la situation pour paralyser la tâche fautive.

En l'absence de pagination, la récupération de la mémoire allouée à un process qui ne la libère pas (par exemple parce qu'il s'est bloqué) supposerait de garder une trace de toutes les allocations, ce qui n'est hélas pas fait (on ne garde que la liste des blocs vides, mais il ne serait pas très difficile de conserver d'autres listes, en dépit de l'encombrement que cela suppose).

On peut utiliser l'allocation à bas niveau, ou passer par Exec qui entretient une liste des blocs libres de façon à limiter la fragmentation (regroupement des blocs contigus, stratégie d'allocation) et autorise la préallocation de blocs d'une certaine taille pour les applications comprenant beaucoup d'allocations dynamiques. On peut choisir d'utiliser la pile de la tâche ou demander de la mémoire publique (pour garantir la visibilité), mais cela n'a pas d'influence dans la mesure ou le système n'utilise pas encore la mémoire paginée.

La pagination de la mémoire consiste à attribuer dynamiquement des "pages" de mémoire à un processus, qu'il croit continues grâce à un système d'adressage en fonction du processus. Cet adressage virtuel suppose une gestion matérielle (MMU), présente dans les processeurs 680x0 au delà du 68020. Ainsi, les processus s'ignorent, sauf s'ils ont des pages communes. A partir de là, il devient quasiment impossible de planter la machine !

On peut alors également implémenter la mémoire virtuelle, en évacuant sur disque les pages inutilisées depuis longtemps pour libérer de la place, ce qui donne l'illusion à l'utilisateur qu'il dispose d'une mémoire bien plus grande, de façon transparente (au temps d'accès près).

AmigaDOS, Intuition, le Workbench et les routines graphiques d'animation s'appuient bien sûr fortement sur Exec, mais comme on l'a vu le programmeur peut lui-même avantageusement s'en servir.

Pour résumer en une phrase, on a pu voir ici que l'approche Orienté Objet apporte compacité, flexibilité, sécurité, extensibilité et facilité d'utilisation et de compréhension...

On trouvera plus de détails sur le système d'exploitation dans La Bible De L'Amiga de Micro Application plus les indispensables "Amiga ROM Kernel Reference Manuals" (structurés comme le man d'Unix) dont il existe une version sur disquette, ainsi que dans un article de Tim Holloway paru dans Bytes en janvier 1991 (p. 329-334) pour l'aspect Orienté Objet.


[Retour en haut] / [Retour aux articles]