Stratus VOS」カテゴリーアーカイブ

プロセス

プロセスは異なるメモリー空間で動作するプログラムです。利用時にログインしている端末もログインプロセスといわれるプロセスの1つです。提供されるコマンドやユーザープログラムをログインプロセス上で動作させて利用していきますが、バックグランドで動かすことも可能です。start_processコマンドを使うと、指定したコマンドをバックグランドで動かすことができます。パラメータを多く指定する必要がありますが、それぞれ意味を持ちます。重要なパラメータの1つにOUTファイルの指定があります。これはバックグランドで動いた場合に出力される標準出力のファイル指定です。実行結果はこのファイルへ書き出され、結果の検証などに使われます。
ログインプロセスも標準出力を持っており、list_port_attachmentコマンドでプロセスが使用しているポートを一覧すると、最初のほうに表示されるTERMINALが相当します。つまり、コマンドを実行した結果は実行した端末に表示されるということになります。

よく利用されるバックグランドプロセスは、起動したらキューや何らかの外部要因で処理を行い、停止するまで実行し続けます。停止はユーザーがプログラムした手法(キューで停止命令などを送って処理させたりいろいろ)で行いますが、停止させるコマンドもstop_processというコマンドで用意されてます。例えば、list_usersコマンドを定期的に実行するコマンドマクロを用意してバックグランドプロセスで実行させておき、データが取れたと思ったらstop_processで停止させ、出力されたOUTファイルを解析してパフォーマンス測定を行ったりできます。
プロダクツとしてプログラミングする場合は、バックグランドで動かすプログラムを起動・停止するプログラムを用意しておき、ユーザーに提供して利用してもらう形になります。この辺りの作りこみが必要ですが、細かく作れば動作状況もわかりやすく設計できると思いますし、満足されるプログラムになるでしょう。

当然ながらstart_processコマンドではなく、APIとしてのルーチンも提供されております。s$start_processがそうで、これもパラメータを多く必要としますがユーザープログラムから任意のプログラムを起動させることが可能です。また、プロセスを扱うプログラムはプロセスが起動時に割り当てられる「プロセスID」を使うことで当該プロセスの情報取得・操作も可能です。当然自分がいま使っているログインプロセスの情報も取れますし、異なるプロセスの情報も取得可能です。ただし、アクセス権がないと拒否されます。

プロセス間通信:シグナル

Stratus VOSもLinuxなどでも存在するシグナルをサポートしてます。s$enable_condition、s$signal_conditionなどのAPIが提供されており、シグナルを受け取ると自前のルーチンへ制御を渡すことができます。PL/Iを使用する場合、言語としてシグナルハンドラーを記述することができますので、言語の機能で実装するのもアリかと思います。

シグナルは使いどころが難しく、何でも処理しようとルーチンを書いた場合、エラー調査が難しくなったり、情報を取り損ねるとデバッグが困難になるケースがあります。プロセスは障害などでシグナルを発生させた場合、デフォルト動作にて動作するようになってます。大抵は端末につながるプロセスなら継続するか停止するかなどの問い合わせを行うようになってます。常駐型のプロセスならキープモジュール(コアダンプ)を作成して停止するようになってます。デバッガを起動してデバッグもできますし、エラーメッセージから状況を判断することも可能です。これらのシグナルも自前のルーチンで処理しようとすると二度手間になる場合がほとんどかと思われます。
まあ、常時別のプロセスに状況を送るようなプログラムで、異常状態を検知した場合に当該プロセスへ通知させてから停止なんてプログラムの場合にはいいのかも。

プロセス間通信:共有メモリー

異なるプロセス間でメモリーを共有する手法として共有メモリー(SVM)が提供されます。Linuxなどで言う共有メモリと同じです。ファイルシステムとプログラムもメモリアドレスを合わせておくことで利用可能となります。
プログラムはSVMへ接続すれば通常のメモリアクセス(構造体や配列など)と区別なくアクセスができます。複数プロセスが同時にアクセスしますから、メモリの排他制御の問題が発生します。各プログラム間で特定領域をアクセスする時に排他制御をそれぞれ行う必要があります。OSのバージョンによっていろいろ変わりましたが、自分が知りうるバージョンではスピンロックのサブルーチンが提供されました。この参照場所を合わせてロック・アンロックすることで排他制御を可能にしてました。

プロセス間通信:キュー

キューは先入れ先出しのプロセス間通信です。送る側をリクエスター、受け取り側をサーバと名付けており、通常はリクエスター・サーバ構造となります。キューはメッセージキュー、1Wayサーバキュー、2Wayサーバキュー、ダイレクトキューが存在します。よく使うのは最初の2つ、場合によっては2Wayサーバキューも利用します。APIはs$msg_xxxという名前のルーチンが提供されてます。

メッセージキューはサーバが動作してようかしてまいが送り込めるキューです。プリンターのキューによく使われており、印刷のリクエストを送ってスプーラーが動作してれば印刷されますし、動作してなければリクエストは溜まっていくという感じです。ファイルI/Oに類似しますが、先入れ先出し機能で最初に投入された要求から処理できるのが大きなメリットです。

1/2Wayサーバキューはサーバが動作して受け入れ可能になってる場合に利用できます。サーバはメッセージが到着すると受信処理を行いメッセージを受け取ります。1Wayサーバキューはリクエスタがメッセージを送るとリクエスタ側は処理が完了します。一方的に送り付けるような処理に向いてます。2Wayサーバキューはリクエスタがメッセージを送信しても、サーバが受け取り完了の通知を行わないと処理完了を待たされます。確実に受け渡しを行いたい場合に使われます。

ダイレクトキューはあまり使ったことがないので各自で調べてみてください。
Stratus VOSにおいて、キューは重要な機能の1つで使いこなせると高度なプログラミングが可能です。

プロセス間通信:イベント通知

Stratus VOSにも異なるプロセスの間でデータを共有ややり取りする手法が提供されてます。共有や受け取りを行うには事前にプログラミングしておく必要があります。任意のプログラム同士でデータをやり取りというのは難しいです。

簡単な通信の1つにイベント通知があります。AプロセスからBプロセスが通信を行うとすると、Aプロセスからイベント通知APIを呼び出すとBプロセスへ通知され、Bプロセスはイベント通知のイベントが発生します。当然Bプロセスはイベント通知を受けるためにイベント通知のイベントを待っている状態でなければなりません。イベントは実ファイルを使って処理されます。最初に実ファイルパス名を決定し、s$attach_eventというAPIを呼び出してイベントの用意をします(ABプロセス双方とも)。Aプロセスは通知にs$notify_eventというAPIを呼び出すことで通知処理を行います。Bプロセスはイベントの用意後に戻されたIDとカウントを使用してs$wait_eventにてイベントを受けるまで待機します。(他のイベント待ちする処理も行っておく)Aプロセスから通知されるとs$wait_eventから抜けてくるので、s$read_eventというAPIでイベント通知を受けます(当然の処理として、待機する何のイベントが発生したのかチェックする必要がありますが)。これで通知されたことによる各種処理を実施することになります。イベント通知には4バイト(fixed bin(31)サイズ)のイベントデータの受け渡しも可能です。これを超えるデータを渡したい場合は、別の手段を使う必要があります。

イベント通知は、何らかのファイル作成をしてその完了を通知するとか、簡単なプロセスへの制御などで使われる可能性があります。まあ、ファイルも巨大でなく、レコード単位の受け渡しだとキュー通信を使った方が処理が簡単になります。また、プロセス間通信ではないですが、タイマーイベントという一定時間経過したらOSから通知してね・・・みたいな使い方もできます。イベントの用意をしたら、s$set_time_eventというAPIで秒数を指定してイベントを待機し、通知されたイベントを判定してタイマーイベントなら当該処理を行うという使い方をします。イベント通知が不要になったらs$detach_eventでリソースを解放します。プログラム終了時にも解放されますが、自分が使ったリソースは自分で解放した方がマナーがいいと思いますし、ルーチンを別のプログラムへ流量したときに解放し忘れがなくなると思われます。

同期・非同期モードとイベント

久々にVOSの話でも。

Stratus VOSのI/O動作には同期モードと非同期モードがあります。ファイルを読み書きするときは通常デフォルトの同期モードで行われ、オペレーションが完了すればI/O動作も終わってる状態となります。短時間で処理される動作なら同期モードでも支障ありませんが、通信などでいつ読み込まれるかわからない場合では同期モードでは処理が止まってしまいます。
例えば、内部的なコマンドを受け付け処理と外部からのデータ受信処理を行うプログラムを作る場合、2つの事象(イベント)を同時に処理する必要があります。これが行われてないと、外部データを読み込みしてる間に内部コマンドが届いても処理できなくて困ってしまいます。そこで登場するのが非同期モードです。
非同期モードは通信やキューなどのプロセス間通信で使用されます。ファイルI/Oでも使用できますが逆に処理が煩雑になります。読み取りで威力を発揮し、読み取り動作を行うとデータがあればデータを戻し、無い場合は受信のイベントを待てという指示としてe$caller_must_waitというエラーコード(実コードは1277)を戻します。そしてプログラムは受信などの動作が完了時に通知されるイベント通知をもって処理再開を行います。イベントを待つのにs$wait_eventというAPIを使用し、処理が完了するとイベント通知されたかわかるという仕組みです。上記の例のプログラムだと、内部コマンドを受け取るイベントと外部データを受け取るイベントの2つを待ち、発生したイベントを判断してそれぞれの処理を行えばいいわけです。これで外部データ受信待ち中に内部コマンドでプログラム停止命令などを受けると正常に終了させられたりできるわけです。
VOSではポートを介して一貫したイベント処理とI/O処理を提供してくれるので、あらゆるI/O動作を簡単に処理できます。これが通信に強いとされる根幹です。

ファイルI/O

Stratus VOSでファイルI/Oを行うには次の手順でアクセスします。
1.ポート接続(s$attach_port)
2.オープン(s$open)
3.I/O(s$xxx_read,write)
4.クローズ(s$close)
5.ポート切断(s$detach_port)

この手順は、ファイルに限らず通信プログラムを書く場合も流れが同じです。まあ、プロトコルが違えばいろいろと設定が必要なので多様なルーチンコールが必要になりますけど。ポートを介してのアクセスという点からではこの流れという。

s$attach_portに指定する必須パラメータは、パス名のみ。パス名はシステムが認識するフルパス名(システム名、ディスク名、ファイルパスをすべて含む名前)を指定しなければ失敗します。よく使われるのは、s$expand_pathルーチンで相対パス名を指定してフルパス名を取得して与えるという感じです。ポート名はNULL文字列を指定すると、ランダムな名前を付けてくれるので、NULLでも問題ないです。まあ、プロダクツを作るなら名前を決めておくのが間違いないですし、誤って複数回attachさせてしまっても間違いに気づきやすいですので。戻される重要な値に、ポート番号があります。以後、ファイルアクセスには、割り当てられて戻されたポート番号を使います。(C言語でいうFILE*みたいなものです)

s$openでファイルをオープンしますが、既存のファイルを読み込みのみでオープンする場合は非常にパラメータがシンプルになります。書き込みや更新(読み書き)の場合は、ファイル属性やレコード長などのパラメータが必要です。あと難易度を高めてるパラメータにロッキングモードがあり、ファイルやレコード単位のロック機構を利用する場合、ファイルを利用するシステム全体で統一しなければなりません。ログみたいなファイルを読み込みモードかつロック無視なら、大半のパラメータは指定しても無視されるので、簡単にオープンできます。

ファイルI/Oはファイル属性やキー順ファイルなどによって異なります。s$rel_で始まるルーチンは固定・相対ファイルに使用し、s$keyed_で始まるルーチンはキー順ファイルに使用します。s$seq_で始まるルーチンはシーケンシャルファイルといった感じです。それと、ロッキング機能も併せてプログラミングしなければなりません。この辺りが一番の肝ですかね。

s$closeでファイルをクローズします。書き込みを行ってる場合は、最終的なフラッシュも行われます。ここで再度オープンすれば再度アクセスすることもできます。

s$detach_portでポートを切断します。この呼び出しで、ファイルを切り離しポートの接続情報をクリアします。

VOSでHELLO WORLD

PL/Iで提供される表示機能にput命令がありますが、これらの機能はいろいろなルーチンを経由し、最終的にVOSネイティブなs$write_codeというルーチンに置き換えられます。結局ネイティブなルーチンになるのなら直接記述したほうがメモリ節約、スピードアップに繋がりますので、PL/I機能を使わない方が有効と思われます。最終的な仕様・ルールは現場に従うしかないのですが。

s$write_codeはシンプルなルーチンで、第一引数の文字列を出力するだけです。第二引数は出力時のエラーコードです。出力先は、標準出力にあたる端末画面です。start_processされた常駐プロセスでは、起動時に指定される.outファイルというシーケンシャルファイルへ出力されます。厳密にいうと、端末ではTERMINALという名前のポート(通常、ポート番号5番)、.outファイルはs$attach_portされたファイルのポートへ出力となります。まあ、出力はとどのつまりポートへの出力なのです。
出力される文字列は、asciiコードのテキストで、バイナリの数値は文字列変換しないとバイナリコードのまま出力されます。バイナリデータの出力は、別途用意されているs$dump、s$dump_portを使うと16進ダンプをテキスト出力することができます。(dump_recordコマンドで出力される形式と同じ。dump_record自体がs$dump_portを使ってると思われます。ちなみに、dump_recordsというコマンドは自分が考案したコマンドで、マルチファイル、ロッキングモードを自動で合わせるコマンド。会社の業務とは別に個人の時間で作ったものなので所有権・著作権を主張できるけど、十分報酬をもらったと思うから置いてきた。)

call s$write_code(‘Hello, world’, error_code);

このように記述すれば、端末画面にHello, worldと表示されると思います。
ちなみに、s$writeというエラーコードがないAPIもあります。こちらの方が手軽なのですが、エラーが発生したときにコアダンプしますので、あまりお勧めしません。エラーコードの戻り先がないので、エラー情報を出して停止シグナル処理されてしまうのです。エラーが出る典型例としては、X.25やダイヤルアップして接続された端末で出力がポーズなどで停止している時にバッファオーバーフローなどで出力できない場合に発生します。telnet上ではエラーを見たことがないですが、通信上何らかのエラーが発生した場合に起こりえます。まあ、個人的なツールなどでは考慮しなくてもいいと思いますけどね。それに、画面出力でエラーが出たからと言ってそのあとの処理をどうするかとか判断が難しいと思います。大抵は通信エラーで、通常では発生しませんし、メイン業務でのエラーの方が問題になると思いますので。なので、通常、エラーが戻されたら無視するのがいいかも。ある意味、s$writeを使って、エラー時はコアダンプする方がもしかしたら安全なのかもしれません。

ソフト設計で重視してたこと

今でも自分が作ったパッケージが動いてるかどうか知りませんけど、末永く動くプログラムを設計するのに重要なのは安全・安定性を重視してました。その機種独特な言語だと移植性も低いですけど、SEQUOIA(UNIXもどきマシン)で設計したパッケージはC言語で書いてましたが、機種依存なものを使わずに設計してたので、IBM RS/6000にソースをそのまま持ってきて無修正で動かすこともできました。まあ、パッケージ自体がコンパクトでシンプルだったのもありますけど。Cライブラリも、バッファオーバーフローを引き起こすようなものは使わず、面倒でも安全なものを選定した記憶があります。

辞める数年前、hp NonStop Serverへのパッケージ移植に携わりましたが、耐障害性の問題もあったので、メーカーに安全なAPIを選定してもらって設計しました。社内へ提供したとき、注意点も含めて公開したにも関わず、各プロジェクトで自由にAPIが使われてたのには驚いた。まあ、辞める直前だったからどうでもいいやって思ってた。それと、汎用的に作っていたプログラムも、プロジェクト用に手直ししろとか言われて、それはプロジェクト側がうちらのプログラムをラップして作るのがルールなんだよって言っても聞いてくれなかったのが痛かった。結局知識を持ち合わせない人が上流工程に入って動かしてるとこうなるよなって例を見せられて反論しようにも、辞めるからどうでもいいやって気持ちが強かったから姑息(その場しのぎ)で仕上げて辞めましたよ。あとはどうにでもなれって思ってた。
Linuxなどでも開発してると思うけど、ライセンスとか守ってるのか疑問です。まあどうでもいいけど。

自分がやってた時は、例えばprintfがNGだったら同じ機能を持ったルーチンを安全なAPIで作ってました。そういった努力が見れられなかったので非常に危険だなって思ってたけどどうなることやら。GPL2なものも勝手に取り込んだりしてないか気になる。

VOSプログラミングの基礎

VOSでプログラミングを行う時の基礎を。コンパイラはPL/I、COBOL、Pascal、Fortran、BASIC、VOS Cと使えますが、COBOL、Pascal、Fortran、BASICは別途パッケージ購入が必要なので入ってないケースが大半だと思います。VOS CはTCP/IPでのソケットプログラミングで必須言語なので、導入されてるケースが多いです。PL/Iは入ってないと話にならない必須言語なので、PL/Iで記述するのが無難です。Emacsなどのエディタでソースを入力して書き込み、コンパイル、バインド(リンク)してプログラムモジュール(.pm)を作成して実行します。ソースをWindowsで作成した場合は、FTPのテキストモードで転送すればシーケンシャルファイルになると思います。(ストリームファイルでもコンパイル可能ですが、あまりお勧めしません)
また、UNIXなどのシェルスクリプトに相当するコマンドマクロもソース同様シーケンシャルファイルで作成します。オープンソース系ソフトを入れていれば、Perlなども使える可能性があります。これらのソースもシーケンシャルファイルで作ります。