Suivez-nous sur Mastodon

|
|
|
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 notifications
(Article écrit par Frédéric Delacroix et extrait d'Amiga News - octobre 1994)
|
|
On le sait déjà, les progrès faits par AmigaOS depuis sa version 2.0 sont immenses. Parmi
les nouveautés, la notification répond à un besoin lié à la nouvelle gestion des Préférences
à partir de cette version : elle permet de détecter les changements effectués sur un fichier.
Dans le cas des Préférences, c'est le programme IPrefs qui se charge de cette surveillance.
Le principe
Sur Amiga, les lecteurs de disquette, le disque dur, le RAM Disk, tout ce qu'on peut imaginer,
sont gérés par des processus séparés. Ainsi, DF0: gère le lecteur interne, RAM: le RAM Disk,
RAD: le disque récupérable, SER: le port série, etc. Le rôle de ces processus, appelés "handlers",
est de gérer les périphériques, soit directement, soit par l'intermédiaire des périphériques logiques d'exec.
La dos.library est ainsi, en gros, un protocole de communication entre les processus :
le programme et les handlers, d'où l'apparition des fameux paquets DOS (DosPackets), que nous n'étudierons
pas en détail ici.
Certains handlers, comme PRT (imprimante) ou SPEAK (le narrateur) n'acceptent que des données brutes,
ce n'est évidemment pas le cas de ceux qui gèrent des fichiers. Le code s'occupant alors de ces
handlers s'appelle alors un "filesystem" (système de fichiers). Sur Amiga, deux systèmes de fichiers
se trouvent déjà en ROM : le premier gère soit l'OFS (Old File System, utilisé surtout avant le
Kickstart 2.0) ou le FFS (Fast File System, version aux performances améliorées).
Le FFS est apparu avec le Kickstart 1.3, et résidait sur disquette (répertoire L:),
on ne pouvait pas amorcer une disquette FFS. De nos jours (Kickstart 2.0 et plus),
l'OFS et le FFS ont été réunis dans un seul module, situé en ROM, d'où son nom de
"ROM FileSystem" (système de fichiers en ROM). Le second système de fichiers se trouve au sein même du RAM-handler,
le gestionnaire du RAM Disk.
Le Workbench 2.1 a également apporté le système de fichiers CrossDOS, qui permet d'accéder aux
disquettes au format MS-DOS comme s'il s'agissait de disquettes Amiga. D'autres système de fichiers
peuvent prendre en compte un lecteur de CD, ou d'autres formats étrangers, on peut trouver le
ProFileSystem dans le domaine public (on peu bogué me semble-il) ; on a ici un aperçu de la
puissance du système de l'Amiga.
En fait, les systèmes de fichiers et les handlers ont le même rôle : recevoir et interpréter les
messages des autres processus qui leur parviennent sous forme de paquets DOS : lecture, écriture,
ouverture de fichier, etc. Les processus nommés DF0:, DF1:, DH0: sont ainsi de multiples
incarnations du système de fichiers en ROM, chargés chacun du périphérique logique DOS correspondant. Ce qui nous concerne
ici est le fonctionnement de l'un de des paquets DOS, apparu avec le Kickstart 2.0, et
plus précisément son interface avec la dos.library : la notification.
La notification consiste à demander à un handler (chargé d'un périphérique logique DOS) de vous prévenir
lorsqu'un objet à sa charge est modifié (par vous-même ou une autre tâche, peu importe).
Pour l'instant, il n'est pas possible de surveiller les lectures. Précisons tout de suite
que les systèmes de fichiers ne sont absolument pas obligés d'autoriser la notification.
Le système de fichiers en ROM et le RAM-handler l'utilisent (pas sans bogues mineurs, d'ailleurs),
pas CrossDOS.
Les opérations suivantes peuvent déclencher une notification : écriture dans un fichier (dans
ce cas, la notification intervient à la fermeture du fichier), création d'un fichier, création
d'un répertoire, effacement d'un fichier, création de liens matériels (hard links),
changement de nom, de commentaire, de date ou d'attributs de protection. De plus, la
notification est associée au volume et non au périphérique logique. C'est-à-dire que si vous enlevez la
disquette avec le fichier surveillé et en insérez une autre, les fichiers de celle-ci ne
sont pas surveillés. C'est comme si, par exemple, DF0: désignait la disquette en DF0:
et non le lecteur lui-même.
Concrètement
Toute demande de notification passe obligatoirement par une structure, définie dans dos/notify.i,
fort judicieusement nommée "NotifyRequest".
struct NotifyRequest {
UBYTE *nr_Name;
UBYTE *nr_FullName; résolu par StartNotify()
ULONG nr_UserData;
ULONG nr_Flags;
union {
struct {
struct MsgPort *nr_Port; pour SEND_MESSAGE
} nr_Msg;
struct {
struct Task *nr_Task; pour SEND_SIGNAL
UBYTE nr_SignalNum;
UBYTE nr_pad[3];
} nr_Signal;
} nr_stuff;
ULONG nr_Reserved[4];
ULONG nr_MsgCount;
struct MsgPort *nr_Handler;
};
|
Voyons les champs importants de cette structure :
"nr_Name" contient un pointeur sur le
nom de l'objet (fichier ou directory) à surveiller. Ce nom sera étendu (en résolvant les
assignations et liens) par la fonction "StartNotify()", un pointeur sur ce nom complet
sera alors disponible dans le champ "nr_FullName".
"nr_Flags" contient quelques drapeaux (flags) qui indiquent au handler comment procéder.
Les drapeaux définis indiquent si la notification doit être faite par l'envoi d'un message
(drapeau NRF_SEND_MESSAGE) ou d'un signal (drapeau NRF_SEND_SIGNAL), si le handler doit attendre
la réponse avant d'envoyer une nouvelle notification (drapeau NRF_WAIT_REPLY,
valable uniquement avec NRF_SEND_MESSAGE) et s'il doit envoyer une notification tout de suite (drapeau
NRF_NOTIFY_INITIAL). Vient ensuite une union. Rappelons que dans ce cas, seule la taille du plus
grand des champs est réservée au sein de la structure. Dans le cas du drapeau NRF_SEND_MESSAGE,
on trouve un pointeur sur le MsgPort où le message doit être envoyé (champ "nr_Port"),
ou, dans le cas de NRF_SEND_SIGNAL, l'adresse de la tâche à signaler et le numéro
du signal (et pas le masque correspondant !) à utiliser.
Il va de soi que le signal doit avoir été alloué par AllocSignal() avant ! Les autres champs
sont privés et doivent être mis à 0. Une fois la structure NotifyRequest réservée et initialisée,
on peut le déclarer grâce à la fonction suivante de la dos.library :
succès.StartNotify(NR)
D0 D1
|
A partir de ce moment, la structure NotifyRequest ne doit plus être modifiée : en effet, le
handler peut y accéder à tout moment pour envoyer des messages. À propos, voyons la structure des
messages reçus dans le cas du flag NRF_SEND_MESSAGE :
struct NotifyMessage {
struct Message nm_ExecMessage;
ULONG nm_Class;
UWORD nm_Code;
struct NotifyRequest *nm_NReq;
ULONG nm_DoNotTouch;
ULONG nm_DoNotTouch2;
};
|
Outre le message Exec placé au début, le seul champ important pour nous est "nm_NReq" :
il pointe la structure NotifyRequest concernée, ce qui est bien pratique si on surveille
beaucoup d'objets à la fois. La raison d'être des champs "nm_Class" et "nm_Code" est l'imitation
d'une structure IntuiMessage, d'où la possibilité d'utiliser le port IDCMP d'une fenêtre pour
la notification. Ce n'est toutefois pas recommandé.
La valeur retournée par StartNotify() indique si le handler a ou non accepté votre demande de
notification. Contrairement à ce que racontent les autodocs, si StartNotify() a échoué, il ne
faut pas appeler la fonction suivante. Celle-ci permet de retirer une demande de notification :
la structure NotifyRequest (passée en argument) pourra alors être libérée ou réutilisée.
Bogues ?
Comme je l'ai dit plus haut, la notification est légèrement boguée dans les versions actuelles des
deux systèmes de fichiers en ROM, rien de très grave, rassurez-vous.
Premièrement, la version 36 (Kickstart 2.0) du système de fichiers en ROM n'envoie pas de
notifications du tout, mais tout le monde devrait posséder au moins la version 37 de nos
jours. De plus, le système de fichiers en ROM n'accepte de notification sur un répertoire
que si celui-ci existe déjà au moment de StartNotify(), la création d'un fichier en MODE_READWRITE
ne déclenche pas de notification, et EndNotify() ne marche pas correctement sur les fichiers situés
sur une disquette non insérée dans un lecteur. Le ram-handler, quant à lui, n'envoie pas de notification si un
fichier est modifié dans un répertoire surveillé (alors que cela constitue aussi une modification du
répertoire en question).
Enfin, le système de fichiers en ROM ignore (pour la notification) SetComment(), SetFileDate()
et SetProtection(). Le RAM handler ignore SetComment() et SetProtection().
Programme d'exemple
Nous en savons assez pour écrire un programme de surveillance de fichiers et de répertoires.
Pour corser un peu, je l'ai prévu pour surveiller plusieurs objets en même temps,
ce qui montre un peu la gestion des listes.
Une dernière chose : sachant que tous les systèmes de fichiers n'implémentent pas la notification
(en particulier CrossDOS et tous les systèmes de fichiers de réseaux), il vaut mieux ne pas
trop se reposer dessus et prévoir, lorsqu'il s'agit de relire un répertoire ou de recharger
un fichier, une action manuelle. De plus, on peut aussi détecter les changements effectués sur
un répertoire en comparant les dates de modifications (ce que font certaines requêtes de fichiers).
Sur ce, je vous laisse avec le listing du programme, à bientôt.
include /macro/exec.i
include exec/types.i
include exec/exec_lib.i
include exec/memory.i
include dos/dos.i
include dos/dos_lib.i
include dos/notify.i
include devices/trackdisk.i
; une structure formée d'une structure MinNode pour le chaînage
; et une structure NotifyRequest:
STRUCTURE MyNotify
STRUCT mnr_Node,MLN_SIZE
STRUCT mnr_NotReq,NotifyRequest_SIZEOF
LABEL mnr_SIZEOF
ENDSTRUCT MyNotify
lea DOSName(pc),a1
move.l #37,d0 ; Kickstart 2.04
CALL OpenLibrary,4.w ; macro/exec.i
move.l d0,_DOSBase
beq exit
lea Notify.List(pc),a0
NEWLIST a0 ; initialise la liste
CALL CreateMsgPort
move.l d0,Notify.Port
move.l #Args.Template,d1 ; lecture des
move.l #Args.Array,d2 ; arguments
moveq #0,d3
CALL ReadArgs,_DOSBase(pc)
move.l d0,Args.RDArgs
beq NoArgs
move.l Args.Array(pc),a5
SetNot move.l (a5),d0 ; fichier suivant
beq .Done
move.l #mnr_SIZEOF,d0
move.l #MEMF_PUBLIC!MEMF_CLEAR,d1 Trouvez comment corriger cette erreur
move.l 4.w,a6
CALL AllocMem
move.l d0,a2 ; réservation de structure
move.l a2,d0
beq.s .NoMem
move.l Notify.Port(pc),mnr_NotReq+nr_Port(a2)
move.l #NRF_SEND_MESSAGE,mnr_NotReq+nr_Flags(a2)
move.l (a5),mnr_NotReq+nr_Name(a2)
move.l a2,d1
add.l #mnr_NotReq,d1
CALL StartNotify,_DOSBase(pc)
tst.w d0
beq.s .NoNotify
move.l a2,a1
lea Notify.List(pc),a0
move.l 4.w,a6 ; ajout à la liste
CALL AddTail
move.l #NotifOK.MSG,d1
move.l a5,d2
CALL VPrintf,_DOSBase(pc)
bra.s .Suiv
.NoNotify
move.l #NoNotify.MSG,d1
move.l a5,d2
CALL VPrintf
move.l a2,a1
move.l #mnr_SIZEOF,d0
move.l 4.w,a6
CALL FreeMem
bra.s .Suiv
.NoMem move.l #NoMem.MSG,d1
move.l a5,d2
CALL VPrintf,_DOSBase(pc)
.Suiv addq.l #4,a5
bra SetNot
.Done move.l Notify.Port(pc),a2
moveq #0,d0
move.l #SIGBREAKF_CTRL_C,d7 ; signal break
move.b MP_SIGBIT(a2),d0
bset d0,d7 ; signal du port
MainLoop
move.l d7,d0
move.l 4.w,a6
CALL Wait
bclr #SIGBREAKB_CTRL_C,d0
bne.s Break ; est-ce un signal d'arrêt ?
NextMes move.l a2,a0
move.l 4.w,a6
CALL GetMsg
move.l d0,a1
move.l a1,d0
beq.s MainLoop
move.l nm_NReq(a1),d2 ; structure NotifyRequest
CALL ReplyMsg
move.l #ObjChanged.MSG,d1
add.l #nr_FullName,d2 ; champ FullName
CALL VPrintf,_DOSBase(pc)
bra.s NextMes
Break lea Notify.List(pc),a0
move.l 4.w,a6
CALL RemHead
move.l d0,d2 ; libère les structures de
beq.s LibArgs ; la liste Notify.List
move.l d2,d1
add.l #mnr_NotReq,d1
CALL EndNotify,_DOSBase(pc)
move.l d2,a1
move.l #mnr_SIZEOF,d0
move.l 4.w,a6
CALL FreeMem
bra Break
LibArgs move.l Args.RDArgs(pc),d1
CALL FreeArgs,_DOSBase(pc)
NoArgs move.l _DOSBase(pc),a1
move.l 4.w,a6
CALL CloseLibrary
exit moveq #0,d0
rts
_DOSBase dc.l 0
Notify.Port dc.l 0
Args.RDArgs dc.l 0
Args.Array dc.l 0
Notify.List dcb.b MLH_SIZE,0
DOSName dc.b 'dos.library',0
Args.Template dc.b "FILES/M/A",0
NotifOK.MSG dc.b "Notification pour %s réalisée.",10,0
NoNotify.MSG dc.b "Pas de notification pour %s!",10,0
NoMem.MSG dc.b "Pas de mémoire pour %s!",10,0
ObjChanged.MSG dc.b "Objet %s changé!",10,0
|
|