Passer au contenu principal

Le système d'exploitation OpenVOS fournit une interface de programmation d'applications (API) de haut niveau qui, dans l'ensemble, facilite la programmation du système. Mais parfois, cela devient trop facile, car un simple appel de sous-programme à une routine s$… peut cacher une grande complexité.

Il s'agit du premier d'une série d'articles irréguliers visant à attirer votre attention sur ces pièges afin que vous puissiez les éviter dans la conception de vos applications.

Les inconvénients des messages de 25 lignes

La possibilité d'écrire un message sur la25e ligne (ou la dernière ligne) d'un terminal permet à un utilisateur (ou à un programme en cours d'exécution) d'avertir les autres utilisateurs de conditions inhabituelles. L'interface de ligne de commande est la commande send_message. La commande et les programmes utilisateur invoquent finalement la sous-routine API s$send_message.    Les arguments de cet appel fournissent le nom d'utilisateur du destinataire, le nom du module cible où cet utilisateur pourrait être connecté, le texte de la notification et certains indicateurs permettant de modifier certains aspects de l'opération. Les appelants privilégiés sont autorisés à spécifier le nom de l'expéditeur ; sinon, le nom d'utilisateur du processus d'envoi est utilisé par défaut.

Jusqu'ici, tout semble simple. Étant donné que l'opération nécessite l'accès au terminal appartenant à un autre utilisateur, le noyau envoie la requête dans une file d'attente du serveur pour le processus TheOverseer sur le module cible. Normalement, le processus d'envoi attend ensuite le statut de livraison du message, mais il peut choisir de ne pas attendre cette réponse.

Le processus Overseer de tout module assure le suivi de tous les processus de connexion et des terminaux utilisés par ces processus.   Lorsqu'il reçoit une demande d'envoi de message, il recherche dans sa liste de processus et de périphériques, et pour chaque nom d'utilisateur de processus correspondant à l'argument receiver_name, il attache un port au périphérique terminal du processus, ouvre le port, écrit le message sur la ligne de notification, ferme le port et détache le port. Si plusieurs processus correspondent à l'argument receiver_name, ce cycle d'attachement/ouverture/écriture/fermeture/détachement est répété pour chacun d'entre eux.

Bon, c'est un peu plus compliqué que ce dont nous avions parlé au départ. En approfondissant la question, nous constatons que n'importe quel module du système peut héberger des processus de connexion où le terminal réel est un périphérique sur un autre système/module qui a utilisé Open StrataLink (OSL) pour se connecter à travers le réseau (avec la commande login –module).   Désormais, les opérations apparemment simples d'attachement/ouverture/écriture/fermeture/détachement sur ce terminal doivent impliquer des processus OSL et des appels de procédure à distance sur le réseau vers le module distant, ce qui est beaucoup plus coûteux que le traitement sur le module.

L'amplification des effets se produit lorsque le nom d'utilisateur destinataire est spécifié comme un nom d'étoile pouvant correspondre à plusieurs ou à tous les utilisateurs connectés. Elle est encore amplifiée si le module cible est également spécifié comme un nom d'étoile. Le pire scénario serait d'envoyer le message à l'utilisateur « *.* » (tous les utilisateurs, tous les groupes) sur le module « * » (tous les modules du système actuel).

Mise en œuvre

s$expand_module_starname(target_module) =>  module_list;

foreach module_name in module_list
   send_overseer_request(module_name, send_message_request_data)
   The message is sent via a server queue and OSL to TheOverseer process
   on that module.

   TheOverseer  process on each receiving module then does:
   foreach terminal_process in TheOverseer’s list of
      login processes and sub-processes;

      if (terminal_process user_name matches receiver star name)
         if (messages_queued_for_device < 5)
            queue it for the login device
         else
            reject the request (e$too_many_terminal_messages)
   Utilisez simultanément jusqu'à 10 ports à la fois pour envoyer tous les messages en attente
        aux terminaux ;
   Pour chaque message pour chaque terminal
      Attacher le port
      Ouvrir le port
      s$control(…WRITE_SYSTEM_MESSAGE…)
      Fermer le port
      Détacher le port

Ce traitement est effectué en mode no_wait_mode pour chaque terminal afin que les retards (tels que les requêtes d'E/S sur le réseau) n'affectent que le traitement des messages pour ce terminal.   


Si un message ne peut être livré dans les 300 secondes, il est supprimé.

Lorsqu'une application utilise ce mécanisme pour signaler des erreurs et qu'une avalanche d'erreurs survient, l'application peut facilement se retrouver submergée par les rapports, alternant entre l'attente du traitement d'autres messages et l'inondation des terminaux des utilisateurs avec des messages qui arrivent trop rapidement pour être compris.

Évitement

Maintenant que vous savez ce qui se passe en coulisses, voici les conseils que je peux vous donner :

  • Choisissez soigneusement les noms d'étoiles pour les noms d'utilisateur et les noms de module afin de vous assurer que vous atteignez uniquement les utilisateurs qui doivent recevoir le message et aucun autre. Par exemple, John_Doe.* ou *.Operations ou John_Doe.SysAdmin.
  • Envisagez de mettre en place une liste d'utilisateurs sans nom d'étoile pour envoyer chaque notification séparément.
  • Placez les détails des messages d'erreur ailleurs (dans un journal des erreurs, par exemple) et utilisez s$send_message pour signaler uniquement la présence d'erreurs.
  • Limitez la fréquence des notifications à un niveau raisonnable (peut-être pas plus d'une fois par minute).
  • Supprimer tout message identique au dernier envoyé. Le nouveau message remplacerait simplement le précédent.

Si vous avez d'autres suggestions, veuillez poster vos commentaires afin que les autres puissent les voir.