Il sistema operativo OpenVOS fornisce un'interfaccia di programmazione delle applicazioni (API) di alto livello che nel complesso semplifica la programmazione del sistema. Ma a volte diventa troppo facile, perché una semplice chiamata di subroutine a una routine s$... potrebbe nascondere una grande complessità.
Questo è il primo di una serie irregolare di post volti a richiamare la vostra attenzione su tali insidie, in modo che possiate evitarle nella progettazione delle vostre applicazioni.
I mali dei messaggi della venticinquesima riga
La possibilità di scrivere un messaggio sullaventicinquesima riga (o su qualsiasi altra riga in fondo) di un terminale consente a un utente (o a un programma in esecuzione) di segnalare ad altri utenti condizioni insolite. L'interfaccia della riga di comando è il comando send_message. Il comando e i programmi utente richiamano infine la subroutine API s$send_message. Gli argomenti di questa chiamata forniscono il nome utente del destinatario, il nome del modulo di destinazione in cui l'utente potrebbe essere connesso, il testo della notifica e alcuni flag per modificare alcuni aspetti dell'operazione. I chiamanti privilegiati possono specificare il nome del mittente; in caso contrario, viene utilizzato come impostazione predefinita il nome utente del processo di invio.
Fin qui sembra tutto semplice. Poiché l'operazione richiede l'accesso al terminale di proprietà di un altro utente, il kernel invia la richiesta su una coda del server per il processo TheOverseer sul modulo di destinazione. Normalmente, il processo di invio attende lo stato di consegna del messaggio, ma può scegliere di non attendere questa risposta.
Il processo Overseer per qualsiasi modulo tiene traccia di tutti i processi di login e del dispositivo terminale utilizzato da tali processi. Quando riceve una richiesta di invio di un messaggio, cerca nel proprio elenco di processi e dispositivi e, per ogni nome utente del processo che corrisponde all'argomento nome_destinatario, collega una porta al dispositivo terminale del processo, apre la porta, scrive il messaggio nella riga di notifica, chiude la porta e scollega la porta. Se più processi corrispondono all'argomento nome_destinatario, questo ciclo di collegamento/apertura/scrittura/chiusura/scollegamento viene ripetuto per ciascuno di essi.
Ok, è un po' più complicato di quanto avevamo detto all'inizio. Approfondendo la questione, scopriamo che qualsiasi modulo del sistema può ospitare processi di login in cui il terminale reale è un dispositivo su un altro sistema/modulo che ha utilizzato Open StrataLink (OSL) per effettuare il login attraverso la rete (con il comando login –module). Ora, l'operazione apparentemente semplice di collegamento/apertura/scrittura/chiusura/scollegamento su quel terminale deve coinvolgere i processi OSL e le chiamate di procedura remota di rete al modulo remoto, ed è molto più costosa rispetto alla gestione sul modulo.
L'amplificazione degli effetti si verifica quando il nome utente del destinatario è specificato come un nome stellare che potrebbe corrispondere a più utenti o a tutti gli utenti registrati. L'effetto è ulteriormente amplificato se anche il modulo di destinazione è specificato come nome stellare. Il caso peggiore sarebbe l'invio del messaggio all'utente "*.*" (tutti gli utenti, tutti i gruppi) sul modulo "*" (tutti i moduli del sistema corrente).
Attuazione
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)
Contemporaneamente, utilizzare fino a 10 porte alla volta per inviare eventuali messaggi in coda
ai terminali;
Per ogni messaggio per ogni terminale
Collegare la porta
Aprire la porta
s$control(…WRITE_SYSTEM_MESSAGE…)
Chiudere la porta
Scollegare la porta
Questa elaborazione viene eseguita in modalità no_wait_mode per ciascun terminale, in modo che i ritardi (come le richieste I/O attraverso la rete) influenzino solo l'elaborazione dei messaggi per quel terminale.
Se un messaggio non può essere consegnato entro 300 secondi, viene eliminato.
Quando un'applicazione utilizza questo meccanismo per segnalare gli errori e si verifica un'ondata di errori, l'applicazione può facilmente rimanere bloccata nella segnalazione, alternando l'attesa dell'elaborazione di altri messaggi e l'inondazione dei terminali degli utenti con messaggi che arrivano troppo velocemente per essere compresi.
Evitamento
Ora che sai cosa succede dietro le quinte, il consiglio che posso darti è:
- Crea con cura nomi di stelle per nomi utente e nomi di moduli per assicurarti di raggiungere solo gli utenti che devono ricevere il messaggio e nessun altro. Ad esempio, John_Doe.* o *.Operations o John_Doe.SysAdmin.
- Valuta la possibilità di implementare un elenco di utenti non basato sui nomi utente per inviare ogni notifica separatamente.
- Inserisci i dettagli dei messaggi di errore altrove (ad esempio in un registro degli errori) e utilizza s$send_message per segnalare solo la presenza di errori.
- Limitare la frequenza delle notifiche a un numero ragionevole (magari non più di una al minuto).
- Elimina qualsiasi messaggio uguale all'ultimo inviato. Il nuovo messaggio sovrascriverebbe semplicemente quello precedente.
Se avete altri suggerimenti, vi preghiamo di pubblicare i vostri commenti affinché tutti possano leggerli.
