06.3 タスクの生成(ATTACH/DETACHとIDENTIFY)①

MVS(z/OS)は、マルチ・タスクのオペレーティング・システムです。一般のアプリケーションでも、複数のタスクに処理を振り分けることでより実行効率のよいプログラム構造を持つこともできます。同時に複数のイベント(処理要求)が発生したり、非同期に発生するイベントを処理するプログラムではタスクを分割するプログラム・デザインは珍しいものではありません。しかし、バッチ処理でのマルチ・タスク構造のプログラムはまれで、通常はオンライン処理プログラムなどサーバー的な機能を持つプログラムにおいて主に採用されます。

どういう時にマルチタスクを使うか

実現したい処理がオンラインだから、サーバーのサービスだからということで何でもかんでもマルチ・タスクにするわけではありません。I/O待ち(通信処理のI/Oも含む)の間に他の処理ができるからということでタスクを分けることは決して間違いではありませんが、トランザクション量が増えても集中しても誤りなく正確にマルチ・タスクのプログラムを制御することは決して簡単ではありません。まずは、本当に単一のタスクで制御できないのかを十分に検討し、その上で必要ならマルチ・タスクでのデザインを考える方がいいでしょう。

  • ファンクションでタスクを分ける
  • トランザクション量でタスクを分ける
  • 大まかなプログラムの設計として、機能か量かでタスクを分ける考え方があります。機能(ファンクション)で分けるというのは、オンライン処理などでは端末(クライアント)の接続、切断、送信、受信、ファイルやデータベースのアクセスや更新などの一連の処理をいくつかのパートに分けて、それぞれの処理を担うプログラムをタスクとして独立させる方法です。このようなデザインは一見わかりやすく各タスクに与えられた機能そのものを実現するプログラムも作りやすいのですが、全体を誤りなく正確に動かすためのタスク間の同期制御やデータを受け渡す制御処理が複雑になります。この部分が甘くなったり、抜けてしまうと信頼性を大きく落とすことになりかねません。
    筆者の経験では、機能でタスクを分けるぐらいなら単一タスクのままWAITやEVENTSでマルチ・ウェイトを行い、イベント(非同期待ち合わせのI/Oなど)の完了した順に必要な処理を行い、待ち合わせの必要な処理は要求だけ投げてその後は他のイベントの処理へ回る、という構造(イベント駆動型)の方がすっきりすると考えています。これは筆者自身の考え方であって、実際に機能でタスクを分ける考え方を採用している例も多いです。筆者もプログラムを覚えたての頃は、機能(ファンクション)でタスクを分けることは自然なことと考えていました。随分と前のことですが、富士通のOS開発部門の方と話す機会があったときに「VTAMはシングル・タスクでやってるよ」ということを聞いた事があります。あれだけの大量のデータをあんなに速い速度で処理するシステムがシングル・タスクのプログラム構造で作れるのか、と感心してからいろいろ私なりに考えてみたものです。実際その後に、在籍していたベンダーでTN3270サーバー的なソフトウェア製品をエンハンスする仕事をした際に、それまで機能で分けていた複数タスクを単一タスクに設計変更して作り直した結果、送受信データが抜け落ちるようなバグは無くなり処理パフォーマンスも向上したという経験があります。
    しかしながら、「一連の処理を単一のタスクで処理をする」という設計を行っても、1つのタスクでは大量のトランザクションが集中すれば処理が追いつかなくことも考えられます。サービスを提供するタスクが1つなので、サービスを受けるための待ち行列は長くなってしまいます。このような場合は、「一連の処理」を完結できる単一のタスクをトランザクション集中量などに応じて増やすことでマルチ・タスク化する方法があります。この方法なら、1つのトランザクションの処理は完結するまで同じタスクが処理しますが、そのタスクを10個用意すれば同時に10のトランザクションを並行して処理できます。今日では、プロセッサーに4つあるいは8つといった複数のCPUが実装されていることが多いので、OSによるCPUディスパッチの切り替えによる多重処理に加えCPU数に応じた同時実行も可能になります。

    タスクの生成と実行(ATTACH)および消去(DETACH)

    新たなタスクを生成し、実行が終わったタスクを消去するAPIの使用サンプルです。タスクを生成して実行するにはATTACHマクロを使います。ATTACHも、基本的にはLINKマクロによるプログラムの呼び出しと同様の手順です。ただし、ATTACHしたプログラム(呼び出し側プログラム)とATTACHされたプログラム(呼び出されたプログラム)は、異なるタスクによって同時に実行されることになる点がLINKと大きく異なります。なお、呼び出したプログラムの処理が短い場合は、ATTACH発行元に制御が戻って来た時点で呼び出したプログラムが終了してしまっていることもあり、どうなるかは処理内容とタイミングによります。ATTACH完了時に呼び出したプログラムが終わっているかまだ実行中かは不定であると考え、どちらであってもいいようにプログラムする必要があります。

    ATTACHマクロのキーワードEP(あるいはEPLOC)は、呼び出すプログラムのロードモジュール・メンバー名で、PARAMは呼び出すプログラムへ渡すパラメーターの指定です。さらに、呼び出したプログラムが終了した際の通知方法を指定します。通知方法には、非同期出口ルーチンの起動とECBへのPOSTの2種類があります。どちらを選択するかはプログラム設計次第です。このサンプルでは、非同期出口ルーチンの起動を指定しています。ATTACHされたタスクが終了したタイミングで、OSはETXRキーワードで指定されたプログラムを呼び出して実行します。ATTACHを発行したプログラムの実行を一時停止して割り込んで実行するので「非同期出口ルーチン」と呼ばれます。その他にも細かなパラメーターがありますが、一般のアプリケーション・プログラムであれば省略値でも十分です。
    ATTACHから制御が戻ると、GR1には生成されたサブタスクのTCBアドレス(OSがタスクを制御するためのコントロール・ブロック)が返ります。誤ったパラメーター・リストを指定するなどを除けば、ATTACH処理自体が失敗することはほとんどありません。EPまたはEPLOCで指定したプログラム名が誤っている場合も、ATTACHが失敗するのではなく生成されたサブタスクがS806等でABENDします。サブタスク起動時のABENDをトラップしてリカバリー処理を行う場合は、ESTAIキーワードを使用してESTAE同様の回復ルーチンを用意することもできます。しかし、S806ABENDをトラップするだけならわざわざESTAIを使わなくとも、ATTACH前にLOADやCSVQUERYマクロを利用することでモジュールが存在するかどうかのエラーは事前にチェックできます。サブタスク起動後であれば、サブタスク側でESTAEマクロを使ってリカバリー環境を構築できます。

    ATTACHされたタスクが終了したら、DETACHマクロによってタスクを消去します。タスクの消去とは、TCBの消去でもあります。タスクの終了が非同期出口ルーチンまたはECBにPOSTされても、TCBはまだ残っておりATTACHしたプログラム側ではそのTCBを参照してタスクの終了状態を調べることができます。必要な情報を収集したり、リソースをクリーンアップした後、DETACHマクロによって終了タスクのTCBを消去することを行います。

    ATTACHされたタスク(子タスク)のプログラムとATTACHしたタスク(親タスク)のプログラムは非同期に動きます。異なるタスクのプログラム間でデータの受け渡したり、処理の整合性を正しく取るためにはタイミング合わせや排他制御などのコントロールが必要になります。これらは、WAIT/POST、ENQ/DEQあるいは逐次化命令(CS/CDS)などを利用することで可能です。