OpenVOSオペレーティングシステムは、高水準のアプリケーションプログラミングインターフェース(API)を提供し、全体としてシステムのプログラミングを容易にします。しかし時に、それはあまりにも容易になりすぎることもあります。なぜなら、s$…ルーチンへの単純なサブルーチン呼び出しが、多くの複雑さを隠蔽してしまう可能性があるからです。
これは不定期連載の第一弾であり、アプリケーション設計においてこうした落とし穴を回避できるよう、皆様の注意を喚起するものです。
25行目メッセージの弊害
ターミナルの25行目(または最下行)にメッセージを書き込む機能により、ユーザー(または実行中のプログラム)は異常状態を他のユーザーに通知できる。コマンドラインインターフェースはsend_messageコマンドである。コマンドおよびユーザープログラムは最終的にs$send_message APIサブルーチンを呼び出す。 この呼び出しの引数には、受信者のユーザー名、そのユーザーがログインしている可能性のある対象モジュール名、通知テキスト、および操作の側面を変更するいくつかのフラグが指定されます。特権を持つ呼び出し元は送信者の名前を指定できます。そうでない場合、送信プロセスのユーザー名がデフォルトとして使用されます。
これまでのところ単純明快である。この操作は他のユーザーが所有する端末へのアクセスを必要とするため、カーネルは要求をターゲットモジュール上のTheOverseerプロセス向けサーバーキューに送信する。通常、送信プロセスはメッセージ配信のステータスを待機するが、この応答を待機しない選択も可能である。
TheOverseerプロセスは、あらゆるモジュールにおいて、すべてのログインプロセスとそれらのプロセスが使用する端末デバイスを追跡します。 メッセージ送信要求を受信すると、プロセスとデバイスのリストを検索し、受信者名引数と一致する各プロセスユーザー名に対して、プロセスの端末デバイスにポートをアタッチし、ポートを開き、通知ラインにメッセージを書き込み、ポートを閉じ、ポートをデタッチします。複数のプロセスが受信者名引数と一致する場合、このアタッチ/オープン/書き込み/クローズ/デタッチのサイクルが各プロセスに対して繰り返されます。
了解。これは最初に話した内容より少し複雑です。さらに調査すると、システム内の任意のモジュールがログインプロセスをホストしている可能性があり、その実際の端末は別のシステム/モジュール上のデバイスであることが判明しました。このデバイスはOpen StrataLink(OSL)を使用してネットワーク経由でログインしています(`login -module`コマンドによる)。 このため、一見単純なターミナルへのアタッチ/オープン/書き込み/クローズ/デタッチ操作は、OSLプロセスとリモートモジュールへのネットワーク経由のリモートプロシージャコールを伴う必要があり、モジュール内処理に比べてはるかにコストがかかります。
影響が拡大するのは、受信者 user_name が複数のログインユーザーまたは全ログインユーザーに一致する可能性のあるスター名として指定された場合です。さらに、対象モジュールもスター名として指定されている場合、影響はさらに拡大します。最悪のケースは、モジュール「*」(現在のシステムの全モジュール)に対して、ユーザー「*.*」(全ユーザー、全グループ)にメッセージを送信する場合です。
実装
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回以下)。
- 前回送信したメッセージと同一のメッセージはすべて抑制する。新しいメッセージは単に前のメッセージの上に上書きされるだけである。
他に何かご提案があれば、他の人が見られるようにコメントを投稿してください。
