OpenVOS オペレーティングシステムは、高レベルのアプリケーション・プログラミング・インタフェース (API) を提供しており、全体的にシステムのプログラミングを容易にします。 しかし、時には簡単すぎることもあります - s$...ルーチンへの単純なサブルーチン呼び出しでは、多くの複雑さが隠されてしまうことがあるからです。
これは、このような落とし穴に注意を促す不定期連載の第一回目で、アプリケーションのデザインでそれらを避けることができます。
25行目のメッセージの悪さ
ターミナルの25行目(または何であれ最下段)にメッセージを書き込む機能により、ユーザ(または実行中のプログラム)は、他のユーザに異常な状態を通知することができます。 コマンドラインインターフェイスは send_message コマンドです。 コマンドとユーザプログラムは最終的に s$send_message API サブルーチンを呼び出します。 この呼び出しの引数には、受信者のユーザ名、そのユーザがログインしている可能性のあるターゲットモジュール名、通知テキスト、操作を変更するためのいくつかのフラグを指定します。 特権者は送信者の名前を指定することができ、それ以外の場合は送信プロセスのユーザ名がデフォルトとなります。
ここまでは簡単そうです。 操作は他のユーザが所有する端末へのアクセスを必要とするので、カーネルはターゲットモジュール上の TheOverseer プロセスのサーバキューにリクエストを送信します。 通常、送信プロセスはメッセージの配信状況を待ちますが、この応答を待たないこともできます。
任意のモジュールのためのOverseerプロセスは、すべてのログインプロセスとそれらのプロセスによって使用される端末デバイスを追跡します。 メッセージを送信するための要求を受信すると、プロセスとデバイスのリストを検索し、受信者名の引数に一致する各プロセスのuser_nameに対して、プロセスの端末デバイスにポートをアタッチし、ポートを開き、通知ラインにメッセージを書き込み、ポートを閉じ、ポートを切り離します。 複数のプロセスが受信者名に一致した場合、この attach/open/write/close/deetach のサイクルをそれぞれのプロセスで繰り返す。
さて、これは最初に話したよりも少し複雑です。 さらに調べてみると、システム内の任意のモジュールがログインプロセスをホストしている可能性があることがわかりました。 この場合、一見単純に見える端末の attach/open/write/close/detach 操作には、OSL プロセスとリモートモジュールへのネットワークリモートプロシージャの呼び出しが必要となり、オンモジュールでの処理よりもはるかにコストがかかります。
効果の拡大は、受信機の user_name が複数またはすべてのログインユーザにマッチする可能性のある starname として指定されている場合に起こります。 また、ターゲットモジュールがスターナメとして指定されている場合は、さらに拡大されます。 最悪の場合は、ユーザ"*.*"にメッセージを送信することになります。(すべてのユーザ、すべてのグループ)にメッセージを送信することになります。
実装
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)
同時に、最大10個のポートを使用して、キューに入っている任意の メッセージを端末に送信します。 各端末のための各メッセージについて アタッチメントポート オープンポート s$control(...WRITE_SYSTEM_MESSAGE...) クローズポート デタッチポート この処理は端末ごとにno_wait_modeで行われるので 遅延(ネットワーク上のI/Oリクエストなど)が影響するのは その端末のメッセージ処理を行います。 300秒で配信できないメッセージがあった場合はフラッシュされます。
アプリケーションがこのメカニズムを使用してエラーを報告し、エラーの洪水が発生した場合、アプリケーションは、他のメッセージが処理されるのを待ったり、理解するには速すぎて到着したメッセージでユーザー端末をフラッディングしたりして、簡単に報告に行き詰ってしまうことがあります。
回避
裏事情がわかった今、私ができる指導は
- ユーザー名とモジュール名のスター名を慎重に作成し、メッセージを受け取る必要のあるユーザーだけに届くようにし、他のユーザーに届かないようにします。 例えば、John_Doe.* や *.Operations、John_Doe.SysAdmin などです。
- 各通知を個別に通知し、送信するための非スターネームのユーザーリストの実装を検討してください。
- エラーメッセージの詳細を別の場所に置き(例えばエラーログ)、s$send_messageを使ってエラーの存在だけを知らせるようにします。
- 通知の頻度を合理的なものに制限してください(おそらく1分に1回以上ではありません)。
- 最後に送信されたメッセージと同じメッセージはすべて抑制します。新しいものは前のものと重なります。
他にも何か提案があれば、他の人に見てもらえるようにコメントを投稿してください。