Suivez-nous sur X

|
|
|
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
|
|
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
|
|
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
|
|
A propos d'Obligement
|
|
David Brunet
|
|
|
|
Programmation : La technique de programmation par overlays
(Article écrit par Denis Jarril et extrait d'Amiga News Tech - novembre 1991)
|
|
La technique de programmation par "overlays" permet de disposer d'une application de plusieurs centaines
de kilo-octets sur disque mais en utilisant beaucoup moins en mémoire centrale.
Comment ça marche ?
C'est très simple, tout du moins dans la théorie : on ne charge les routines en mémoire qu'au fur et à
mesure de leur utilisation, et on les libère lorsqu'elles se terminent. Vous vous doutez certainement
que pratiquement, c'est plus compliqué : une routine du programme principal doit s'occuper de libérer
et de charger les nouveaux overlays au moment opportun. Fort heureusement, le compilateur Lattice Lc
et son éditeur de liens associé BLink réduisent au strict minimum le travail du programmeur : celui-ci
n'a plus qu'à simplement prévoir quelles fonctions de son application seront ou ne seront pas des overlays.
Avouez que c'est déjà quand même plus simple.
Une représentation classique des overlays est sous forme d'arborescence, comme dans la figure 1 ci-dessous.
Figure 1
La racine est le programme principal, le premier à être chargé. Quoiqu'il arrive, il réside toujours en
mémoire et ne pourra pas en être viré par le gestionnaire d'overlays.
Les overlay 1 et overlay 2 s'exécutent
indépendamment l'un de l'autre, un seul à la fois : le programme principal ne peut pas appeler de fonctions de
l'overlay 1 puis de l'overlay 2 sans que le gestionnaire ne le charge d'abord. De cet arbre, quelques règles
simplissimes découlent :
- Si l'overlay 1 est chargé, les 2, 3, 4 et 5 ne le sont pas.
- Si l'overlay 2 est chargé, le 1 ne l'est pas, mais 3, 4 et 5 peuvent l'être (ce n'est pas obligatoire).
- Si l'overlay 3 est chargé, le 2 l'est aussi, les 1 et 4 ne le sont pas, et le 5 peut l'être.
- Si l'overlay 4 est chargé, les 1, 3 et 5 ne le sont pas.
- Si l'overlay 5 est chargé, les 2 et 3 aussi, mais les 1 et 4 ne le sont pas.
Malheureusement, quelques restrictions existent :
- La racine peut appeler des routines de n'importe lequel des cinq overlays.
- Les cinq overlays peuvent appeler des routines de la racine.
- Toutes les routines de l'overlay 1 peuvent appeler d'autres routines de l'overlay 1
ainsi que toutes celles de la racine.
- Toutes les routines de l'overlay 2 peuvent appeler d'autres routines de l'overlay 2
ainsi que celles de la racine et des overlays 3 et 4.
- Toutes les routines de l'overlay 3 peuvent appeler d'autres routines de
l'overlay 3 ainsi que celles de la racine et des overlays 2 et 5.
- etc.
On le voit, deux noeuds de même niveau sont mutuellement exclusifs : l'overlay 1 ne peut pas appeler
des routines de l'overlay 2.
Les overlays, pour qui ?
Le mécanisme des overlays n'est pas à utiliser n'importe quand. On le réserve en général pour les
applications réellement énormes (plus de 100 ko).
Imaginons par exemple que vous écriviez un programme de traitement de texte. Le programme principal -
la racine - contiendra toutes les routines d'entrée de caractères, d'éditions, de manipulations de bloc...
Un premier overlay pourrait contenir les routines d'impression, qui n'ont pas besoin d'être chargées en
permanence. Un second overlay pourrait s'occuper de l'impression en PostScript, qui est totalement
différente de l'impression matricielle ou laser (on pourrait également partager le premier overlay en
deux, l'un pour PostScript, l'autre pour les matricielles). Enfin, un troisième overlay pourrait gérer
l'analyse du document (comptage du nombre de mots, de caractères, repérage des répétitions, histogrammes...).
C'est uniquement au programmeur de décider de tout cela.
Les overlay, comment ?
C'est BLink qui se charge de presque tout, lors de la phase d'éditions de liens du programme définitif :
il remplace chaque appel d'une routine appartenant à un noeud différent de celui en cours par un saut au
gestionnaire d'overlays. Celui-ci décide alors, en fonction du niveau des deux noeuds concernés dans l'arborescence,
s'il doit d'abord charger quelque chose depuis le disque ou non. Le gestionnaire est inclus dans les
bibliothèques du Lattice, mais on peut au besoin le remplacer par un fait maison. Ce qui ne présente pas
réellement beaucoup d'intérêt.
BLink utilise un fichier WITH spécial dans sa ligne de commande qui décrit l'arborscence des overlays. Le
format de ce fichier est strictement défini et doit être respecté à la lettre. L'arbre de la figure 1
pourrait être décrit dans le fichier suivant :
OVERLAY
Overlay1
Overlay2
*Overlay3
**Overlay5
*Overlay4
#
|
Le mot-clé OVERLAY introduit le premier niveau de l'arborescence et le signe dièse (#) termine la liste.
La racine n'est pas incluse : BLink utilise comme racine le premier fichier objet de sa ligne de commande
(le fichier FROM ou ROOT). Une astérisque introduit un niveau supplémentaire d'overlay, pas d'astérisque
du tout correspondant au niveau 1. Remarquez enfin que l'overlay 5 a été placé tout de suite derrière le
3, dont il dépend dans l'arbre.
Illustration du fonctionnement des overlays
Voici une petite illustration du fonctionnement des overlays, avec un programme parfaitement inutile,
qui ne fera certainement pas date dans l'histoire de la presse informatique...
I1 faut quand même reconnaître qu'il n'est pas évident de démontrer par un court programme l'utilisation -
mais aussi et surtout l'utilité - des overlays, mais j'ai tout de même décidé de le faire. Les listings
qui suivent font donc partie d'un seul programme nommé "Ovls" (raccourcis pratique pour "Overlays"...).
Dans l'ordre, nous avons la Racine (Racine.c), trois overlays de niveau 1 (0vl1.c, Ovl2.c et Ovl3.c)
et deux overlays de niveau 2 (0vl3_1.c et Ovl3_2.c). Un fichier WITH pour BLink est également inclus,
histoire de montrer comment il est conçu.
Listing 1 : Racine.c
/**********************************************************
* Arborescence des overlays : *
* +----------+ *
* | Racine.c | *
* +----+-----+ *
* | *
* +--------+ +----+---+ +--------+ *
* | Ovl1.c +--+ Ovl2.c +--+ Ovl3.c | *
* +--------+ +--------+ +---+----+ *
* | *
* +----------+ | +----------+ *
* | Ovl3_1.c +-+-+ Ovl3_2.c | *
* +----------+ +----------+ *
*********************************************************/
#include <stdio.h>
/* Fonctions de main.c */
int GetChar(void);
/* Fonctions du premier niveau de l'arbre */
extern void Ovl1(void);
extern void Ovl2(void);
extern void Ovl3(void);
/* fonction main() */
void main(int argc, char **argv)
{
int c = 0;
if (!argc)
return; /* N'accepte pas le WB */
while (c != 'q' && c != 'Q')
{ printf("\014Démonstration du mécanisme des Overlays.\n");
printf("\n*** Ici la Racine\n\n");
printf("1 - Appeler l'overlay 1\n");
printf("2 - Appeler l'overlay 2\n");
printf("3 - Appeler l'overlay 3\n");
printf("Q - Quitter\n");
printf("Appeler quel overlay (1/2/3) ? ");
switch(c = GetChar())
{ case '1':
Ovl1();
break;
case '2':
Ovl2();
break;
case '3':
Ovl3();
break;
default:
break;
}
}
}
int GetChar(void)
{
int c1, c2;
c1 = getchar();
while ((c2 = getchar()) != '\n');
return(c1);
}
|
Listing 2 : Ovl1.c
#include <stdio.h>
extern int GetChar(void);
void Ovl1(void)
{
printf("\n*** Ici l'overlay 1.\n");
printf("Une touche pour retourner à la Racine...");
GetChar();
}
|
Listing 3 : Ovl2.c
#include <stdio.h>
extern int GetChar(void);
void Ovl2(void)
{
printf("\n*** Ici l'overlay 2.\n");
printf("Une touche pour retourner à la Racine...");
GetChar();
}
|
Listing 4 : Ovl3.c
#include <stdio.h>
extern int GetChar(void);
extern void Ovl3_1(void);
extern void Ovl3_2(void);
void Ovl3(void)
{
int c = 0;
while (c != 'r' && c != 'R')
{ printf("\014*** Ici l'overlay 3.\n");
printf("A - Appeler l'overlay 3_1\n");
printf("B - Appeler l'overlay 3_2\n");
printf("R - Retourner à la Racine\n");
printf("Qu'est-ce qu'on fait (A/B/R) ? ");
switch(c = GetChar())
{ case 'a':
case 'A':
Ovl3_1();
break;
case 'b':
case 'B':
Ovl3_2();
break;
}
}
}
|
Listing 5 : Ovl3_1.c
#include <stdio.h>
extern int GetChar(void);
void Ovl3_1(void)
{
printf("\n*** Ici l'overlay 3_1.\n");
printf("Une touche pour retourner à l'overlay 3...");
GetChar();
}
|
Listing 6 : Ovl3_2.c
#include <stdio.h>
extern int GetChar(void);
void Ovl3_2(void)
{
printf("\n*** Ici l'overlay 3_2.\n");
printf("Une touche pour retourner à l'overlay 3...");
GetChar();
}
|
Listing 7 : Makefile
Ovls: Racine.o Ovl1.o Ovl2.o Ovl3.o Ovl3_1.o Ovl3_2.o
Blink WITH Ovls.lnk
Racine.o: Racine.c
Lc Racine.c
Ovl1.o: Ovl1.c
Lc Ovl1.c
Ovl2.o: Ovl2.c
Lc Ovl2.c
Ovl3.o: Ovl3.c
Lc Ovl3.c
Ovl3_1.o: Ovl3_1.c
Lc Ovl3_1.c
Ovl3_2.o: Ovl3_2.c
Lc Ovl3_2.c
|
Listing 8 : Ovls.lnk (fichier WITH pour BLink)
ROOT lib:c.o Racine.o
TO Ovls
LIBRARY lib:lc.lib,lib:small.lib
SMALLCODE SMALLDATA NODEBUG NOICONS
OVERLAY
Ovl1.o
Ovl2.o
Ovl3.o
*Ovl3_1.o
*Ovl3_2.o
#
|
Mise à jour de mai 2025 : une archive contenant le listing adapté à Lattice C, et avec l'exécutable
compilé, a été réalisée par Yann-Gaël Guéhéneuc et est disponible sur
obligement.free.fr/files/antoverlays.lha.
|