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 : Assembleur - les SegList-splits
(Article écrit par Frédéric Delacroix et extrait d'Amiga News - mai 1994)
|
|
Comment faire pour qu'un programme, une fois lancé à partir du CLI sans la commande "Run",
puisse rendre la main à celui-ci, lui permettant de lancer un autre programme, ou même de
se fermer ? Cet article va décrire une solution, certes pas unique, mais de loin la plus utilisée.
Généralités
Lorsqu'un programme est stocké sur disque, outre les données du programmes proprement dites, il
existe des informations annexes. Le fichier exécutable est ainsi divisé en segments, chacun décrivant les
données qu'il contient. Sans entrer dans les détails, disons que chaque segment contient les données de
chaque segment du programme, c'est-à-dire les morceaux du programme séparés par la directive "Section"
pour la plupart des assembleurs, ainsi que des informations de relogement nécessaire (pour que le
programme fonctionne à partir de n'importe quelle adresse en mémoire).
On peut également trouver quelques données additionnelles tels que les noms des symboles ou des numéros de
lignes destinés à un débogueur (c'est ce système qu'utilise l'option "HCLN" de Devpac 3).
Une fois que le programme a été chargé en mémoire par le DOS, le relogement est automatiquement
effectué par la fonction LoadSeg(). La division en segments subsiste toutefois, et
ceux-ci peuvent se trouver éparpillés dans la mémoire. Pour que le DOS retrouve ses
billes lors de la libération de mémoire à la fin du programme, il code quelques informations
pour chaque segment. On trouve ces informations juste avant le début du code programme de
chaque segment (repéré ici par le décalage 0) :
- Décalage -4 : pointeur BCPL sur le prochain segment (0 si c'est le dernier segment). Ce
pointeur pointe sur le pointeur "prochain segment" du segment suivant. Rappelons qu'à
cause des excentricités originelles du BCPL, tous les pointeurs utilisés par ce langage
pour le moins bizarre sont multiplier par 4 avant d'en faire une utilisation sensée.
- Décalage -8 : mot long qui contient la taille en octets de la zone mémoire réservée
pour ce segment. Cette zone inclut bien entendu ce mot long. Cette valeur n'est utilisée
que par la fonction UnLoadSeg().
La solution
A la lumière de tout ceci, la voie est tracée : il suffit, après quelques initialisations
éventuelles, de "couper" la liste des segments, de démarrer un nouveau processus grâce
à la fonction Create[New]Proc() à partir du second segment et de laisser le premier
se terminer normalement par un RTS. Le DOS libérera alors la SegList avec laquelle il
avait lancé le programme par la fonction UnLoadSeg(), mais comme nous avons pris la
précaution de la couper auparavant, la zone mémoire occupée par le second processus -
celui que nous venons de démarrer - ne sera pas libérée, et celui-ci pourra tourner en
paix. Lorsqu'il voudra se terminer, un simple RTS suffira. Enfantin, non ?
Quelques précautions élémentaires de programmation s'imposent toutefois : une fois la
partie résidente du programme lancée, celle-ci ne doit pas chercher à accéder aux
données qui étaient contenues dans le premier segment, étant donné qu'il s'agit alors
de mémoire libérée. Ceci étant précisé, nous pouvons passer à l'écriture d'un programme d'exemple.
Un exemple
Pour avoir un bon confort de programmation, nous nous restreignons aux versions supérieures
ou égales à la version 37 (Kickstart 2.04) du système d'exploitation. On pourrait - non sans
un peu de mal - adapter cette méthode aux versions antédiluviennes du système, sur
lesquelles la fonction CreateNewProc() de la dos.library n'existe pas.
Le programme commence par ouvrir la dos.library V37. Si cela réussit, il sauvegarde alors
le contenu du pointeur "prochain segment" (trouvé à l'adresse Start-4) et met un 0 à
sa place. Physiquement, il n'existe plus aucun lien entre le premier segment et les autres.
On démarre ensuite le processus à partir du pointeur que l'on avait sauvegardé grâce à
la fonction CreateNewProc(). Examinons au passage cette fonction et ses arguments.
Elle ne prend qu'un argument, une liste de marqueurs, en D1. Les marqueurs utilisables sont les suivants :
- NP_SegList : indique la SegList à utiliser pour démarrer le nouveau processus.
C'est ce que nous avons utilisé dans notre cas.
- NP_Entry : indique le point d'entrée pour le nouveau processus.
Celui-ci ne possèdera pas de SegList. L'un des marqueurs NP_SegList ou NP_Entry est nécessaire.
- NP_FreeSegList : marqueur booléen indiquant s'il faut libérer ou pas la SegList à
la fin du programme. Attention : dans les auto-docs, il est mentionné que la valeur par
défaut de ce marqueur est "TRUE", ce qui est faux ! Il faut absolument fournir ce marqueur
pour ne pas perdre la mémoire !
- NP_Arguments : pointe sur la chaîne de caractères qui sera passée en paramètres
au nouveau processus.
- NP_Input, NP_Output, NP_Error : indiquent les FileHandles des canaux
respectivement d'entrée, de sortie et d'erreur. Par défaut, NIL: est utilisé.
- NP_Closelnput, MP_CloseOutput, NP_CloseError : marqueurs booléens indiquant s'il
faut ou non fermer les canaux correspondant à la fin du processus.
- NP_CurrentDir : indique, par un FileLock, le chemin courant du processus.
- NP_StackSize : indique la taille de sa pile (4000 par défaut).
- NP_Name, NP_Priority : indiquent son nom ("New process" par défaut) et sa
priorité.
- NP_WindowPtr : ce marqueur est très important dans l'utilisation que nous en
faisons. En effet, lorsque le DOS doit afficher une requête système (dans le style
"Le volume XXX est protégé en écriture"), il regarde le pointeur pr_WindowPtr
de la structure Process concernée. S'il est valide, ce champ pointe sur une fenêtre
qui sera utilisée pour déterminer l'écran où il faut afficher la requête. S'il
est nul. l'écran public par défaut ("Workbench" en général) est utilisé, s'il est
égal à -1, aucune requête n'est affichée. Dans la plupart des cas, le processus associé
à un CLI a son champ pr_WindowPtr nul, mais il arrive que des petits programmes le
remplacent, afin de ne pas avoir à revenir au Workbench si on utilise un CLI sur un
autre écran.
Le problème, c'est que le processus que nous créons ne peut pas être sûr que la
fenêtre CLI du processus père est encore ouverte. Donc le champ pr_WindowPtr risque
de contenir une valeur invalide s'il est non nul, d'où un gourou assuré en cas de
requête système. Il faut donc le mettre à 0 dans notre cas.
- NP_Cli : indique s'il faut créer une nouvelle structure CLI pour le
nouveau processus (non par défaut).
- NP_ConsoleTask, NP2FlomeDir, NP_Path : dans le cas où une structure CLI
est associée au nouveau processus, ces marqueurs indiquent le processus de console, le
tiroir d'origine et le chemin d'accès.
En retour de la fonction CreateNewProc(), on obtient un pointeur sur la structure
Process nouvellement créée (rappelons qu'un processus est une tâche au sens Exec
du terme avec quelques extensions, lui permettant notamment d'appeler le DOS), ou
0 si la création a échoué (par manque de mémoire, absence de l'un des marqueurs NP_Entry
ou NP_SegList...). Dans notre cas, on teste si la création a réussi.
Si c'est le cas, nous laissons le processus fils fermer la dos.library que nous avons
ouverte, sinon nous la fermons nous-même.
Le processus fils est très simple : il se contente d'ouvrir une fenêtre CON:, d'attendre
un caractère et de sortir en fermant tout derrière lui.
Une dernière remarque : même si le code est écrit en réentrant (c'est-à-dire utilisable
par plusieurs tâches à la fois), un programme utilisant ce stratagème pour se détacher
du CLI ne peut pas être rendu résident, pour la simple raison que la liste des programmes
résidents est gérée en gardant des pointeurs sur les SegLists, cette même SegList
que nous avons sauvagement sectionnée.
Sur ce, je vous laisse avec le listing du programme... La prochaine fois, nous verrons
comment lire les arguments à partir du CLI, et les types d'outils à partir du Workbench.
|