Ceci est une ancienne révision du document !
1
In the early days of computers, a company called Digital Equipment Corporation (DEC) created its 32-bit VAX computer using openVMS as its operating system. Because a VAX/VMS computer is so reliable, there are today - after more than 25 years - still a large number of them in use. But, in the end, even these reliable computers will have to be replaced. As described in part 1, you could migrate from VAX/VMS to Linux, as the way Linux works is largely compatible with VAX/VMS. If you use Pascal as your programming language, you will find that Lazarus/Free Pascal is a good replacement. But there are technical functions used in VMS with no apparent replacement in Linux. In this article I will describe how eventflags can be replaced. The art of migration The best way to do a migration is to leave the source code as much unchanged as possible. This saves work, but, more important, it prevents errors caused by changes. When you encounter a function with no counterpart in Linux, you could rewrite that part of the code, or you implement the function. The latter is what I have done with functions concerning eventflags. Let me start by explaining what eventflags are and what they are being used for.
Aux débuts de l'informatique, l'entreprise Digital Equipment Corporation (DEC) a créé ses ordinateurs 32-bit VAX avec openVMS comme système. Les ordinateurs VAX/VMS sont si fiables qu'ils sont aujourd'hui un grand nombre à être encore utilisés, après plus de 25 ans. Mais finalement même ces ordinateurs fiables devront être remplacés. Comme je l'ai décrit en partie 1, vous pouvez migrer de VAX/VMS vers Linux, car la façon dont travaille Linux est largement compatible avec VAX/VMS. Si vous utilisez Pascal comme langage de programmation, vous trouverez que Lazarus/Free Pascal est un bon remplaçant. Mais il y a des fonctions techniques de VMS qui n'ont pas de remplaçant évident sur Linux. Dans cet article je vais décrire comment les eventflags (drapeaux d’évènements) peuvent être remplacés.
L'art de migrer
La meilleure façon de migrer est de laisser le code source aussi inchangé que possible. Cela économise du travail, et plus important, ça évite les erreurs provoquées par les changements. Si vous êtes en présence d'une fonction n'ayant pas d'équivalent en Linux, vous pouvez soit réécrire cette partie de code, soit implémenter cette fonction. C'est ce que j'ai fait avec les fonctions concernant les drapeaux d'évènements. Je vais donc commencer par expliquer ce que sont ces drapeaux et pourquoi on les utilise.
What are eventflags? Eventflags are, in essence, global booleans. The reason why they are important is the fact that they are used consistently throughout the entire system in VMS, including kernel and drivers. Their main function is synchronization between processes and drivers, processes and other processes, or even within a process, between threads. But they can also be used to maintain (system-wide) statuses or as a binary semaphore. An eventflag can be set or cleared implicitly or explicitly. Implicitly when used while accessing a device (cleared upon starting and set when done), or explicitly using the kernel routines $SETEF and $CLREF. When set or cleared with the kernel routines, the kernel will return the previous state of the eventflag as its result. This makes it possible to use the eventflag as a binary semaphore, as only 1 process can set the flag and get the result “WASCLEARED”, the others get as result “WASSET”. The VMS kernel uses the indivisible VAX machine code instruction test-and-set to do this. On a PC this would be the BTS instruction. Free Pascal does not contain a corresponding function, so I had to implement it in assembly (machine language). I didn't have this much fun programming since I created my own - functionally identical - version of CP/M (something like DOS) back in the 80's….
Que sont les drapeaux d'évènements?
Les Eventflags sont, par nature, des booléens globaux. La raison pour laquelle ils sont importants est qu'ils sont utilisés fréquemment partout dans le système VMS, et même dans le noyau et les pilotes. Leur fonction principale est la synchronisation entre des processus et des pilotes, entre processus eux-mêmes et même à l'intérieur d'un processus, entre les threads. Mais ils peuvent être utilisés pour sauvegarder des états (globalement sur le système) ou comme sémaphores binaires.
Un drapeau peut être levé ou abaissé (enlevé) implicitement ou explicitement. Implicitement quand on l'utilise pour accéder à un périphérique (abaissé au début et levé quand c'est fait), ou explicitement en utilisant les appels système $SETEF et $CLREF. Quand on utilise les appels système, le noyau va renvoyer l'état précédent du drapeau comme résultat. Ca permet de les utiliser comme sémaphores binaires, puisque seulement 1 processus peut lever le drapeau et obtenir le résultat «WASCLEARED» (EtaitAbaissé), les autres auront le résultat «WASSET» (EtaitLevé). Le noyau VMS utilise l'instruction machine atomique, indivisible, test-and-set de VAX pour le faire. Sur un PC ça serait l'instruction BTS. Le Free Pascal ne contient pas de fonction similaire, donc j'ai du l'implémenter en assembleur (langage machine). Je n'avais pas eu autant de plaisir à programmer depuis le jour où j'ai créé ma propre version de CP/M isofonctionnelle (quelque chose comme le DOS) dans les années 80…
2
How are eventflags used? Only setting or clearing would be useless without reading the result. You can do this by asking the kernel if a certain eventflag is set or cleared ($READEF), but its main use is going to sleep and waiting for an eventflag to be set ($WAITFR). You can use this - for example - if you have to analyze data from a file (or the Internet). You can read the first part of the file and - to save time - immediately give the command to read the next part while you analyze the data you've just read. When done, you might have to wait until the next part of the file is read. You have specified an eventflag to be set when done in your read command, so now you put your process to sleep until reading is done and the eventflag is set. In VMS this is an integrated part of the kernel. You can even wait for one of a number of eventflags (within the same cluster) to be set (multiple events, $WFLOR). Let's say in the previous example it is possible that the next part will never arrive! Then, you could start a timer that will set another eventflag when it runs out of time, and you specify that you want to sleep until either one of those two eventflags are set. When the process (or thread within a process) wakes up, it reads the eventflags to see if the data has arrived or the timer has run out (do not forget to cancel the timer if the data has arrived before the timer runs out!). As the kernel is updating the eventflag as well as waking up the process, this is one of the fastest ways to respond to an event.
Comment utilise-t-on les drapeaux?
Ne faire que lever ou abaisser un drapeau serait inutile si on ne savait pas lire le résultat. Vous pouvez le faire en demandant si tel drapeau et levé ou non ($READEF), mais l'usage est d'attendre qu'un drapeau soit levé ($WAITFR). Vous pouvez le faire, par exemple, si vous devez analyser des données d'un fichier (ou d'Internet). Vous pouvez lire la première partie du fichier et, pour gagner du temps, demander de lire immédiatement la partie suivante du texte pendant que vous analysez les données qui viennent d'être lues. Une fois ceci fait, il se peut que vous ayez à attendre que la partie suivante soit lue. Si vous avez dédié un drapeau à être levé quand c'est terminé, vous pouvez alors mettre en sommeil votre processus jusqu'à ce que la lecture soit terminée et que le drapeau soit levé. Pour VMS, ceci est intégré au noyau.
Vous pouvez même attendre que l'un des drapeaux parmi plusieurs dans le même groupe soit levé (évènements multiples, $WFLOR). Dans l'exemple ci-dessus, imaginons qu'il soit possible que la prochaine partie du texte n'arrive jamais! Alors, vous pouvez déclencher un sablier qui lèvera un autre drapeau quand il aura fini, et vous spécifiez que vous voulez vous mettre en sommeil jusqu'à ce que l'un des deux drapeaux soit levé. Lorsque le processus (ou le thread) se réveille, il lit les drapeaux pour voir si les données sont arrivées ou si le sablier est épuisé (n'oubliez pas de réinitialiser le sablier si les données sont arrivées avant qu'il ne soit épuisé). Comme le noyau met à jour l'état du drapeau et que c'est lui qui réveille le processus, c'est une des manières les plus rapides de réagir à un évènement.
In VMS, all access to devices goes through a unified system call named QIO (Queued Input / Output). First you have to create a link to a device to get a handle. Each device has a different way to do this: To access a physical device directly, you use “assign”; to use a virtual device - such as a mailbox (IPC, described in part 4) - you use a kernel function to create an instance of that device. Then you use this handle in the QIO call to specify which device you want to access. As the name implies, the kernel uses queues to store your request, a separate queue for every device. This means you do not have to wait for the request to finish. Therefore this function exists in 2 flavors: The $QIO for asynchronous access, and $QIOW if you want to wait for the result. In both cases you have to specify an eventflag (the default is eventflag 0), used to signal that the request is executed. To implement asynchronous calls to functions that are synchronous in Linux, I've used threads. The call to QIO creates a new thread in which the synchronous function is used, the eventflag is cleared and the program continues. When the function is ready, the eventflag is set and the thread is closed. This way, the program behaves on Linux as it did on VMS. Fortunately, you do not always have to use the QIO call. There exist higher level calls that do the complex stuff for you, but the penalty is that this cannot be done asynchronously. As an example: Reading from or writing to a file is done with the VAX-Pascal “open” statement (in Free Pascal: assign), followed by reset/rewrite and readln/writeln (the same as in Free Pascal).
Pour VMS, tous les accès aux périphériques passent par un appel système unifié, le QIO (Queued Input / Output). D'abord vous devez créer un lien vers un périphérique pour obtenir un pointeur (handle). Chaque périphérique a une manière différente de le faire: pour accéder à un périphérique physique directement, vous utilisez «assign»; pour utiliser un périphérique virtuel, comme une boite aux lettres (IPC, décrit dans la partie 4), vous utilisez une fonction du noyau pour créer une instance de ce périphérique. Ensuite vous utilisez ce pointeur dans la commande QIO pour spécifier quel périphérique vous voulez atteindre.
Comme leur noms l'indiquent, le noyau utilise des queues (files d'attente) pour ranger vos demandes, une queue différente pour chaque périphérique. Ainsi vous n'avez pas à attendre que la demande se termine. Mais il existe deux saveurs pour cette fonction: le $QIO pour les accès asynchrones, et $QIOW si vous voulez attendre le résultat. Dans les deux cas, vous devez nommer un drapeau (le drapeau par défaut est 0) à utiliser pour signaler que la demande est réalisée.
Pour construire des appels asynchrones dans des fonctions qui sont synchrones en Linux, j'ai utilisé les threads. L'appel à QIO crée un nouveau thread dans lequel la fonction synchrone est utilisée, le drapeau est abaissé et le programme continue. Quand la fonction est prête, le drapeau est levé et le thread est détruit. De cette façon, le programme se comporte sur Linux de la même manière que sur VMS.
Heureusement, vous n'avez pas à utiliser tout le temps les appels QIO. Il existe des appels de fonctions de plus haut niveau qui font le boulot complexe à votre place, mais la contrepartie est que ça ne peut pas se faire de manière asynchrone. Par exemple : Lire ou écrire dans un fichier est réalisé avec la fonction «open» de VAX-Pascal («assign» en Free Pascal), suivie de reset/rewrite et readln/writeln (idem en Free Pascal).
3
How many and what kind are available? There are 128 eventflags you can use, numbered from 0 to 127. As they are implemented as 32-bit unsigned integers within the kernel, the eventflags are divided in 4 blocks of 32 each. Because eventflags can be used for different purposes, there are also different eventflags. The first two blocks (or clusters as they are called in VMS) are the process local eventflags (0..31 and 32..63). They can be accessed only by the process itself, and are mainly used when accessing a driver or using a timer. The last two clusters (64..95 and 96..127) are called common eventflags. These clusters exist only when created. When you create a common eventflag cluster, you have to specify a name for the cluster. If separate processes in the same group (Linux: GID) specify the same name, they will reference the same cluster. This way, processes can synchronize: If one is setting an eventflag in such a cluster when done and the other is waiting for this same eventflag to be set. Once created, they are not fixed to your process. You can switch between clusters (in VMS this is called mapping). Be aware that you can map a common eventflag cluster to a different block. In this case, an eventflag mapped to eventflag 64 in one process could be the same as eventflag 96 in another process! You should better avoid this to prevent confusion. Valid only when tested! To test my functions, I created a program to show/change all eventflags and to create common eventflag clusters. In the past, there have been times I would have paid good money to have such a program when working with VMS. This small program will also be available as open source. Next month: In the next article, I will go more in-depth on logicals.
Combien sont disponibles et de quels types ils sont?
Vous pouvez utiliser 128 drapeaux, numérotés de 0 à 127. Comme ils sont définis en entiers 32-bit non signés dans le noyau, les drapeaux sont regroupés en 4 blocs de 32. Comme les drapeaux peuvent être utilisés pour différents buts, il sont aussi de types différents. Les deux premiers blocs (ou clusters selon la terminologie VMS) sont les drapeaux de processus locaux (0..31 et 32..63). Il ne peuvent être manipulés que par les processus, et sont principalement utilisés pour accéder aux pilotes ou utiliser un sablier. Les deux derniers clusters (64..95 et 96..127) sont appellés les drapeaux communs. Ces clusters n'existent que lorsqu'ils sont créés.
Lorsque vous créez un cluster de drapeaux communs, vous devez donner un nom au cluster. Si différents processus d'un même groupe (Linux: GID) donne le même nom, ils vont faire référence au même cluster. Ainsi, les processus peuvent se synchroniser: Si l'un lève un drapeau dans un de ces clusters lorsqu'il a terminé et qu'un autre est en attente de ce même drapeau.
Une fois créés, ils ne sont plus attachés à votre processus. Vous pouvez changer de cluster (en VMS, on appelle çà le mapping). Attention de ne pas faire correspondre un cluster de drapeau commun cluster sur un bloc différent. Car dans ce cas, le drapeau correspondant au drapeau 64 dans un processus pourrait être le même que le drapeau 96 d'un autre processus! Vous feriez mieux d'éviter cela pour empêcher la confusion.
C'est bon qu'une fois testé!
Pour tester mes fonctions, j'ai créé un programme qui montre/modifie tous les drapeaux et qui crée les clusters de drapeaux communs. Par le passé, il y aeu bien des fois où j'aurais payé cher pour avoir un programme comme celui-ci quand je travaillais sur VMS. Ce petit programme sera aussi disponible en open-source.
Le mois prochain: Dans le prochain article, je décrirai plus en détail les fonctions logiques.