O sistema operacional OpenVOS fornece uma interface de programação de aplicativos (API) de alto nível que, em geral, facilita a programação do sistema. Mas, às vezes, torna-se fácil demais, pois uma simples chamada de sub-rotina para uma rotina s$... pode esconder muita complexidade.
Esta é a primeira de uma série irregular de publicações para chamar sua atenção para essas armadilhas, para que você possa evitá-las em seus projetos de aplicativos.
Os males das mensagens de 25 linhas
A capacidade de escrever uma mensagem na25ª linha (ou qualquer outra linha inferior) de um terminal permite que um usuário (ou um programa em execução) notifique outros usuários sobre condições incomuns. A interface da linha de comando é o comando send_message. O comando e os programas do usuário invocam, em última instância, a sub-rotina da API s$send_message. Os argumentos para essa chamada fornecem o nome de usuário do destinatário, o nome do módulo de destino onde esse usuário pode estar conectado, o texto da notificação e alguns sinalizadores para modificar aspectos da operação. Chamadas privilegiadas podem especificar o nome do remetente; caso contrário, o padrão é o nome de usuário do processo de envio.
Até agora, parece simples. Como a operação requer acesso ao terminal de outro usuário, o kernel envia a solicitação em uma fila do servidor para o processo TheOverseer no módulo de destino. Normalmente, o processo de envio aguarda o status da entrega da mensagem, mas pode optar por não aguardar essa resposta.
O processo Overseer para qualquer módulo mantém o controle de todos os processos de login e do dispositivo terminal usado por esses processos. Quando recebe uma solicitação para enviar uma mensagem, ele pesquisa sua lista de processos e dispositivos e, para cada nome de usuário do processo que corresponde ao argumento receiver_name, ele anexa uma porta ao dispositivo terminal do processo, abre a porta, grava a mensagem na linha de notificação, fecha a porta e desanexa a porta. Se vários processos corresponderem ao argumento receiver_name, esse ciclo de anexar/abrir/gravar/fechar/desanexar é repetido para cada um.
Ok – isso é um pouco mais complicado do que discutimos inicialmente. Ao explorar mais a fundo, descobrimos que qualquer módulo do sistema pode estar hospedando processos de login em que o terminal real é um dispositivo em outro sistema/módulo que usou o Open StrataLink (OSL) para fazer login pela rede (com o comando login –module). Agora, a operação aparentemente simples de anexar/abrir/gravar/fechar/desanexar nesse terminal precisa envolver processos OSL e chamadas de procedimento remoto de rede para o módulo remoto, e é muito mais cara do que o manuseio no módulo.
A ampliação dos efeitos ocorre quando o nome de usuário do destinatário é especificado como um nome de estrela que pode corresponder a vários ou a todos os usuários conectados. E é ainda mais ampliada se o módulo de destino também for especificado como um nome de estrela. O pior caso seria enviar a mensagem para o usuário “*.*” (todos os usuários, todos os grupos) no módulo “*” (todos os módulos do sistema atual).
Implementação
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)
Simultaneamente, use até 10 portas por vez para enviar quaisquer mensagens enfileiradas para os terminais. Para cada mensagem para cada terminal Anexar porta Abrir porta s$control(…WRITE_SYSTEM_MESSAGE…) Fechar porta Desanexar porta Esse processamento é feito em no_wait_mode para cada terminal, de modo que os atrasos (como solicitações de E/S pela rede) afetem apenas o processamento de mensagens para esse terminal. Se alguma mensagem não puder ser entregue em 300 segundos, ela será descartada.
Quando um aplicativo usa esse mecanismo para relatar erros e ocorre uma enxurrada de erros, o aplicativo pode facilmente ficar sobrecarregado com os relatórios, alternando entre esperar que outras mensagens sejam processadas e inundar os terminais dos usuários com mensagens que chegam rápido demais para serem compreendidas.
Evitar
Agora que você sabe o que está acontecendo nos bastidores, a orientação que posso lhe dar é:
- Crie cuidadosamente nomes de estrela para nomes de usuário e nomes de módulo para garantir que você alcance apenas os usuários que devem receber a mensagem e nenhum outro. Por exemplo, John_Doe.* ou *.Operações ou John_Doe.SysAdmin.
- Considere implementar uma lista de usuários sem nomes de estrelas para notificar e enviar cada notificação separadamente.
- Coloque os detalhes das mensagens de erro em outro lugar (um log de erros, por exemplo) e use s$send_message para alertar apenas sobre a presença de erros.
- Limite a frequência das notificações a algo razoável (talvez não mais do que uma vez por minuto).
- Suprima qualquer mensagem que seja igual à última enviada. A nova mensagem simplesmente substituiria a anterior.
Se você tiver outras sugestões, poste seus comentários para que outras pessoas possam vê-los.
