Obligement - L'Amiga au maximum

Vendredi 18 août 2017 - 07:03  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

 · Accueil
 · A Propos
 · Articles
 · Galeries
 · Glossaire
 · Hit Parade
 · 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 in other languages


Twitter

Suivez-nous sur Twitter




Liens

 · Sites de téléchargements
 · Associations
 · Pages Personnelles
 · Moteurs de recherche
 · Pages de liens
 · Constructeurs matériels
 · Matériel
 · Autres sites de matériel
 · Réparateurs
 · Revendeurs
 · Presse et médias
 · Programmation
 · Développeurs logiciels
 · Logiciels
 · Développeurs de jeux
 · Jeux
 · Autres sites de jeux
 · Scène démo
 · Divers
 · Informatique générale


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

 


Programmation : C/C++ - gestion des ressources par "moniteur" (2)
(Article écrit par Gilles Dridi et extrait d'Amiga News - juillet/août 1997)


Le partage d'une fonction non réentrante - 2e partie

Nous continuons la partie conception. Nous présentons à nouveau la dernière solution où vous remarquerez la paire active()/desactive(). Nous verrons après deux autres solutions et nous vous proposerons de vous amuser avec ces programmes (exa, exb et exbb) en réglant "le débit" des signaux, par l'intermédiaire de l'utilitaire "input". Nous terminerons par une dissertation de la gestion de ressource sur Amiga.

Première solution

Partage de la fonction printf() de la bibliothèque C par masquage des exceptions.

Gestion des ressources

Nous n'utilisons pas l'héritage multiple. Ainsi la classe CompteurMoniteur hérite de Contrôleur Moniteur vues dans "ex.h", et la classe Contrôleur Moniteur hérite de la classe Moniteur qui hérite de Exception2.

On peut vouloir utiliser l'héritage multiple mais c'est un cas où l'on doit utiliser une classe de base virtuelle, car on ne doit appeler le constructeur de classe Exception2, qui installe notre vecteur d'exception, qu'une seule fois. Ceci n'empêche pas d'utiliser la classe Exception2 plusieurs fois, à condition qu'un appel au destructeur soit fait pour remettre en place l'ancien vecteur, après un appel au constructeur. Ceci est fait automatiquement lorsque l'instance de classe est détruite. Si elle est déclarée dans un bloc {...}, c'est en sortie de bloc : "}".

Gestion des ressources

Avec l'héritage multiple et classe de base virtuelle faire :

Moniteur -> virtual Exception2, Contrôleur -> virtual Exception2
Et Compteur -> Moniteur, Contrôleur

Le mot-clé "virtual" permettra d'appeler le constructeur de la classe Exception2, une seule fois au niveau de la classe Compteur.

On appelle la classe Moniteur, et plus généralement cela un moniteur parce qu'il regroupe les fonctions, ici afficheQQCH(), qui partage une ressource, ici la fonction printf() considérée comme non réentrante. Toutefois, un "vrai moniteur" est un mécanisme d'arbitrage de ressources qui ne permet qu'à une seule fonction d'entrer dans le moniteur. Nous avons dit qu'en fait la routine d'exception n'est pas interruptible par la fonction main() puisque les fonctions ne sont pas dans deux tâches différentes, mais dans la même. Ainsi, la gestion de l'accès au moniteur est simplifiée puisque seule Exception2::routine() peut interrompre le main(), pas l'inverse. On a juste à arbitrer l'accès à la fonction printf() du main(). C'est-à-dire, dans cette première solution, on désactive les exceptions dans la fonction afficheValeur() au moment de l'appel à "la ressource printf()". Un signal, qui provoquerait une exception, surgissant durant le printf() de afficheValeur() est masqué jusqu'à active().

Deuxième solution

Par exclusion mutuelle (gestion de la section critique lors du printf().

Gestion des ressources
Gestion des ressources

Dans la classe Mutex qui donne un exemple de "sémaphore binaire", il n'y a pas de paire Forbid()/Permit() autour de l'affectation de la variable "dem" qui ne serait pas atomique par exemple LEA #dem,A0 et MOVE #1,(A0). Une affectation atomique n'est pas nécessaire puisque la routine n'est pas interruptible ; ne redonne pas une chance de s'exécuter au main(). Dans le cas où les deux fonctions peuvent s'interrompre l'une et l'autre, on pourrait, entre ces deux instructions entrer dans routine2() qui testera alors mutex comme non occupé, puis exécute partiellement son printf() et, interrompu par le commutateur, revenir au main() pour exécuter son printf(). Or, on a considéré la fonction printf() comme non réentrante.

Ce que l'on veut avec une fonction primitive comme P() ou V(), c'est qu'un état (la valeur d'une variable en informatique) reflète une action (de l'appel au retour d'une fonction) pendant toute la durée de son exécution. Ainsi, la paire Forbid()/Permit() autour de l'expression conditionnelle : if (relance) ... n'est pas nécessaire pour effectuer l'exclusion, mais, pour que la variable relance ne soit pas infirmée après le test qui l'avait trouvée affirmée (if (relance) **exception** ...) et ainsi maintenir une cohérence entre un état et l'action associée à celui-ci.

L'exclusion est réalisée par les opérations P()/V() du mutex et le test du prédicat estOccupé(). Les faits que le signal soit réaffirmé et l'affichage relancé si la variable relance est affirmée, permet d'effectuer "l'exclusion mutuelle". Toutefois, si une exception survient avant la paire Forbid()/Permit() (c'est-à-dire entre mutex.V() et Forbid()), c'est elle qui "prend la place" de la relance en annulant cette variable.

On utilise, d'une façon générale, l'opération du sémaphore P() pour obtenir l'accès à la ressource et l'autre V() pour libérer la ressource. Ainsi, si nous avons deux tâches qui puissent s'interrompre l'une et l'autre c'est-à-dire "mutuellement", on aura une paire P()/V() autour de chacune des deux sections critiques : codes de qui se partagent la ressource. Un sémaphore binaire peut aussi comme un moniteur bloquer l'accès à une ressource. Nous n'avons pas vu, ici, comment ce blocage et cette libération étaient réalisés mais juste l'interface du sémaphore : P() et V().

Amélioration ?

L'inconvénient de ces deux solutions est qu'elles ne permettent pas de mémoriser le nombre d'exceptions qui pourraient survenir durant l'exécution du printf() du main(), puisque c'est soit un bit (celui du long mot du signal), soit une variable booléenne "dem" qui sont fixés. Toutefois, le but recherché est réalisé, c'est une exclusion mutuelle.

Nous proposons une troisième solution qui se mord un peu la queue, puisqu'elle mémorise rapidement (l'incrémentation d'un compteur) les exceptions pouvant survenir durant l'exécution du printf() du main(), mais fera autant d'affichage après ! Ce ne sont plus des signaux exceptionnels et ce n'est plus du partage de ressource.

On peut améliorer la solution en réduisant la succession de ces affichages en un seul. J'ai ajouté le nombre d'exceptions que l'on peut, par exemple, considérer comme perdues.

Le plus intéressant est le seuil à partir duquel on décide de ne plus traiter de signaux et donc de "perdre", par exemple, des informations. Penser aux positions que nous envoie la souris pour afficher le pointeur à l'écran ou à "l'overrun" d'une liaison série. C'est un autre sujet, celui du "temps réel". On déciderait alors de définir une fréquence d'échantillonnage, on connaîtrait ainsi le nombre d'échantillons par période. On choisira alors un système assez rapide pour ce traitement et on utilisera une horloge (compteur d'événements) pour cadencer l'acquisition des données.

Troisième solution (gadget)

Par exclusion mutuelle et comptage d'exeption.

Gestion des ressources

Remarque : dans l'affichage des programmes exb et exbb, le symbole "0" pour les variables "cpt" ou "relance" signifie que le signal d'exception n'a pas eu lieu pendant la possession de la ressource, c'est-à-dire dans la paire P()/V(). De plus, dans les deux programmes, une exception qui survient entre mutex.V() et le Forbid() est considérée une relance du signal.

On peut s'amuser avec cette "solution gadget" en augmentant le nombre des Ctrl_E à envoyer au programme. Pour cela, on reste appuyé sur Ctrl_E. On peut régler la vitesse de répétition d'une même touche de la console par l'utilitaire "input". On pourra à côté lancer, dans un autre Shell dont la priorité sera plus élevée que le Shell où nous exécutons cette solution, la commande "dir : all" afin de ralentir notre tâche. Ceci nous permettra de recevoir plus de signaux qui déclencheront notre routine et qui les mémorisera pour afficher une rafale plus tard. Voir ci-dessous le pourquoi.

Quelques explications sur les signaux Ctrl_X

La console, c'est-à-dire le périphérique logiciel "console.device", reçoit des informations de "l'input.device" qui est aussi un périphérique logiciel avec une tâche ou pilote ayant une haute priorité. C'est le Shell ou CLI (Command Line Interface) qui est un processus AmigaDOS, ayant ouvert la "console.device", qui reçoit ces signaux d'une tâche nommée "console.device" avec une priorité moyenne ; par l'intermédiaire de la fonction Signal().

Enfin, avec l'utilitaire "input", nous réglons la vitesse de répétition d'une touche et non la vitesse d'émission des codes du clavier qui est toujours la même. Nous n'avons pas expliqué où et comment le traitement de la répétition d'une touche est effectuée.

Conclusion

Portabilité

Ce code n'est portable, c'est-à-dire réutilisable sur un autre système d'exploitation, par une simple recompilation, que si celui-ci dispose du mécanisme du signal d'exception. Ce qui est le cas, je crois, pour Unix. Cette étude nous aura permis d'expliquer pourquoi une routine, appelée suite à un signal d'exception, est similaire à une interruption locale à la tâche. Pour faciliter le portage d'un programme avec ce signal d'exception, nous pouvons utiliser le fichier d'inclusion <signal.h> de la bibliothèque C, dont nous connaissons maintenant les écueils. Par exemple, le système Cami, que nous avons vu dans un article précédent, permet à l'utilisateur d'interrompre l'évaluation d'une expression par Ctrl_C ou Ctrl_D. La solution la plus simple est, comme nous l'avions vu dans l'article sur Exec, d'insérer un test du signal (normal) par l'appel système SetSignal() dans la boucle d'évaluation. Nous avons vu aussi que ce test peut être celui d'une variable de classe spéciale (volatile) fixée de "l'extérieur" par la routine du signal d'exception. Cette technique est plus portable si le système d'exploitation est conforme aux signaux Posix, en particulier à condition d'utiliser le fichier d'inclusion <signal.h> et que son implantation définisse, ici, SIGINT.

Voici l'exemple, remarquez la ligne du compilation sans -dLATTICE, ni -LasmEx.o :

Gestion des ressources
Gestion des ressources

Le modèle client-serveur

Nous pouvons dire que l'exercice, proposé dans la première partie, offre une autre solution à la gestion de ressource par le passage de message. On peut donc éclater les deux programmes qui coexistent dans deux tâches, l'une gérera le signal d'exception comme un signal normal avec une fonction d'attente et aura une priorité supérieure à l'autre tâche, ceci afin de traiter immédiatement le signal comme s'il était un signal d'exception. On déléguera en fait, à Exec et à la bibliothèque AmigaDOS, la partie gestion de ressource par l'intermédiaire des dialogues qui auront lieu avec les périphériques logiciels ou "devices".

De la même manière que cet "éclatement" en tâches, on peut introduire pour deux processus qui veulent partager une ressource, au modèle deux processus + un sémaphore (pour l'arbitrage), un modèle avec un troisième processus : trois processus + du passage de messages soit deux processus et un processus qui gère la ressource. J'ai envie d'appeler celui-là un serveur : un processus et sa file d'attente (plutôt qu'un "manager"). Ainsi, on peut penser que le sémaphore sert à l'arbitrage entre processus lorsqu'il n'y a pas de délégation automatique de la gestion de ressource ; la file d'attente du serveur et sa gestion par des primitives de synchronisation.

Si les mécanismes de sémaphore ou de moniteur permettent d'arbitrer l'accès à une ressource et de bloquer un processus jusqu'à ce qu'il puisse y accéder, le serveur et sa file d'attente de messages, bloque le processus lorsqu'il attend sa réponse à un message enfilé. De plus, les commandes que le serveur comprend permettent de définir une interface évolutive et standardisée de la ressource qu'il gère.

Apparté

La plupart des ressources Amiga (puces spécialisées et périphériques) sont gérées par des périphériques logiciels ou "devices" qui sont implantés, par ce que j'ai appelé, des serveurs. On a vu que la graphics.library contient, entre autres, des fonctions pour gérer le Blitter. Or, on peut remarquer que tous ces "devices", le sont, parce qu'ils ont à faire aux Entrées/Sorties, dites lentes. On pourrait définir qu'une commande, pour un système graphique qui permette de déplacer un objet en détectant les collisions avec les bords de l'écran ou d'autres objets, soit une E/S lentes : Boing, Boing !

Enfin, on peut dire que le message d'interruption (Cf. article Exec) par rapport aux messages est comparable au signal d'exception pour les signaux.

Ce que nous venons de décrire est un modèle client-serveur. Ce modèle permet plus encore que le partage ou l'arbitrage de ressources, d'établir une communication avec un protocole ; déjà vu. Vous pourrez trouver une description du modèle client-serveur dans "Modern Operating Systems" de Andrew S. Tanenbaum 1992. La surprise de cette machine est que l'intérieur de son système d'exploitation simule (émule) une architecture client-serveur grâce au passage de messages. Avec des clients : les programmes utilisateurs et avec des serveurs : les périphériques logiciels, activés directement par les clients ou indirectement ; comme le font les commandes de l'AmigaDOS.


[Retour en haut] / [Retour aux articles] [Article précédent]