5    Tru64 UNIX STREAMS

オペレーティング・システムは,AT&T の System V によって指定された STREAMS フレームワーク,STREAMS のバージョン 4.0 リリースを提供します。 このフレームワークでは,従来の UNIX の文字入出力 (I/O) の代わりに,ユーザは,I/O 関数をモジュール方式でインプリメントできます。 モジュール方式で開発された I/O 関数を使用すると,アプリケーションは,通信サービスを容易に構築したり再構成したりできます。

「STREAMS」はフレームワーク全体を指し,「ストリーム」は,アプリケーション・プログラムが open システム・コールを使用して作成したエンティティを指すことに注意してください。

この章では,次の項目について説明します。

この章では,Tru64 UNIX の STREAMS のインプリメンテーションと,AT&T System V バージョン 4.0 のインプリメンテーションの相違について,詳細に説明します。 Tru64 UNIX のインプリメンテーションが,AT&T のインプリメンテーションと大きく変わらないところについては,AT&T の該当するマニュアルに関する情報を提示します。

この章では,STREAMS フレームワークを使用してプログラムを作成する方法については説明しません。 プログラミング情報についての詳細は,『Programmer's Guide: STREAMS』を参照してください。

5.1    STREAMS フレームワークの概要

STREAMS フレームワークは,次のものから構成されます。

図 5-1では,STREAMS フレームワークを強調表示して,ネットワーク・プログラミング環境における位置付けを示しています。

図 5-1:  STREAMS フレームワーク

5.1.1    STREAMS の構成要素の概説

STREAMS を使用して通信するには,アプリケーションでストリームを作成します。 ストリームは,ユーザ・プロセスとデバイス・ドライバの間の全二重の通信パスです。 ストリーム自体はカーネル・デバイスであり,アプリケーションに対しては文字型特殊ファイルとして表されます。 他の文字型特殊ファイルと同様に,ストリームはオープンしなければなりません。 それ以外の場合は,システム・コールによって操作します。

各ストリームの先頭と末尾には,それぞれ 1 つ以上のストリーム・ヘッドとストリーム・エンドがあります。 ストリームを通して引き渡されるデータの処理に必要な場合には,リンクしたキューのペアから構成される追加モジュールを,ストリーム・ヘッドとストリーム・エンドの間に挿入できます。 データは,モジュール間でメッセージを使用して引き渡されます。

この項では,次の STREAMS の構成要素について簡単に説明します。

また,メッセージについて説明するとともに,STREAMS フレームワーク内におけるメッセージの役割についても説明します。

図 5-2 は,一般的なストリームを示しています。 ストリーム・ヘッドからストリーム・エンド (図 5-2 では STREAMS ドライバ) に流れるデータは,ダウンストリーム,または書き込み方向に流れるといいます。 ストリーム・エンドからストリーム・ヘッドに流れるデータは,アップストリーム,または読み取り方向に流れるといいます。

図 5-2:  ストリームの例

ストリーム・ヘッドは,カーネル内においてユーザ・プロセスとストリームの間にインタフェースを提供する,ルーチンとデータ構造体のセットです。 アプリケーションが open システム・コールを発行すると,ストリームが作成されます。 ストリーム・ヘッドが実行する主なタスクは,次のとおりです。

  1. STREAMS システム・コールの writeputmsg といった標準サブセットを解釈する。

  2. ユーザ空間からの STREAMS システム・コールを,STREAMS メッセージ (M_PROTO,M_DATA など) の標準の範囲に変換する。

    STREAMS メッセージは,データおよび制御情報から構成されます。

  3. メッセージを次のモジュールへダウンストリームに送信する。

    メッセージは,最終的にストリーム・エンド,つまりドライバに到達します。

  4. ドライバからアップストリームに送信されたメッセージを受信して,カーネル空間からの STREAMS メッセージを,アプリケーションによって呼び出されるシステム・コール (getmsgreadなど) に適したフォーマットに変換する。

    フォーマットは,システム・コールによって異なります。

ストリーム・エンドは,STREAMS モジュールの特殊な形式であり,ハードウェア・デバイス・ドライバまたは擬似デバイス・ドライバのいずれかです。 ハードウェア・デバイス・ドライバの場合,ストリーム・エンドは,カーネルと外部通信デバイスとの間の通信を提供します。 擬似デバイス・ドライバの場合には,ストリーム・エンドはソフトウェアでインプリメントされ,外部デバイスには関連しません。 ハードウェア・デバイス・ドライバ,または擬似デバイス・ドライバのいずれであるかにかかわらず,ストリーム・エンドは,その上のモジュールから送信されたメッセージを受信し,解釈して,要求されたオペレーションを実行します。 次に,ストリーム・ヘッドに向かってアップストリームに送信する適切なメッセージ・タイプのメッセージを作成して,アプリケーションにデータおよび制御情報を返します。

ドライバは,次の点を除いて,他の STREAMS モジュールと同じです。

デバイス・ドライバおよびデバイス・ドライバ・ルーチンについての詳細は,『Writing Device Drivers』 および『Programmer's Guide: STREAMS』を参照してください。

モジュールは,データを,ストリーム・ヘッドからストリーム・エンドに引き渡す際と,戻す際に処理します。 ストリームは,データが要求する処理の量と種類に応じて,ゼロ個以上のモジュールを持つことができます。 ドライバが,データに対して必要なすべての処理を実行できる場合には,追加のモジュールは必要ありません。

モジュールは,データをいれる 1 組のキュー,および各モジュールの役割を定義する他の構造体へのポインタで構成されます。 1 つのキューは,ドライバに向かってダウンストリームに流れるデータを処理し,もう 1 つのキューは,ストリーム・ヘッドおよびアプリケーションに向かってアップストリームに流れるデータを処理します。 ポインタは,各モジュールのダウンストリーム・キューおよびアップストリーム・キューを,次のモジュールのダウンストリーム・キューおよびアップストリーム・キューにリンクします。

処理の必要条件に応じて,アプリケーションは,特定のモジュールをストリームにプッシュするように要求します。 ストリーム・ヘッドは,アプリケーションが要求したモジュールをアセンブルした後,モジュールのパイプラインを通してメッセージの経路指定を行います。

情報は,メッセージを使用して,モジュールからモジュールに引き渡されます。 STREAMS 環境において,さまざまなタイプのメッセージが定義されますが,すべてのメッセージのタイプは,次のカテゴリに分類できます。

M_DATA および M_IOCTL などの通常メッセージは,受信した順に処理されて,STREAMS フロー制御およびキュー登録機構に従います。 優先メッセージは,ストリームを通して優先的に引き渡されます。

メッセージおよびメッセージ・データ構造体についての詳細は,5.3.2 項を参照してください。

5.1.2    ioctl プロセス

STREAMS では,ユーザ・プロセスは,ストリーム内で ioctl を呼び出して,特定のモジュールとドライバを制御することができます。 ユーザ・プロセスが ioctl コマンドを実行する際に,STREAMS はプロセスをブロックし,強制的にストリーム・ヘッドがコマンドを処理するようにします。 また,必要に応じてメッセージ (M_IOCTL) を書き込み方向に送信して,特定のモジュールまたはドライバが受信して処理するようにします。 ユーザ・プロセスは,次のいずれかが行われるまでブロックされます。

STEAMS には,ioctl を処理するメソッドとして,I_STR と透過の 2 つがあります。 表 5-1 で,その 2 つのメソッドの比較をしています。

表 5-1:  ioctl 処理のI_STR メソッドと透過メソッドの比較

I_STR 処理 透過処理
STREAMS ファイルに対して作成されたアプリケーションのみをサポートする。 STREAMS ファイルか,非 STREAMS ファイルのいずれに対してでも作成されたアプリケーションをサポートする。
streamio(7) に記述されているコマンド群が利用できる。 ioctl(2) に記述されているコマンド群が利用できる。
データ・フォーマットおよびアドレッシングに制限がある。 データ・フォーマットおよびアドレッシングの制限は ioctl に依存する。
ioctl の処理を完了するには,1 対のメッセージが必要である。 ioctl の処理を完了するために,複数対のメッセージが必要になることがある。
タイムアウトの値はユーザ定義または省略時の値。 タイムアウトはしない。
STREAMS 処理を前提とする。 もっと汎用的。

ioctl 処理に対する両方のメソッドについての詳細は,『Programmer's Guide: STREAMS』 を参照してください。

5.2    STREAMS に対するアプリケーション・インタフェース

STREAMS フレームワークに対するアプリケーション・インタフェースによって,STREAMS メッセージは,アプリケーションで送受信できます。 以降の項で,STREAMS ヘッダ・ファイルおよびデータ型へのポインタを含むアプリケーション・インタフェースについて説明するとともに,STREAMS および STREAMS 関連のシステム・コールについても説明します。

5.2.1    ヘッダ・ファイルおよびデータ型

基本的な STREAMS のデータ型の定義は,次のヘッダ・ファイルに含まれています。

注意

一般に,ヘッダ・ファイル名は山カッコ (< >) で囲まれています。 ヘッダ・ファイルの絶対パスは,山カッコ内の情報の前に /usr/include/ を付けたものです。 <sys/stream.h> の場合,stream.h/usr/include/sys ディレクトリに存在します。

5.2.2    STREAMS 関数

アプリケーションは,次の関数を使用して,STREAMS のカーネル・リソースにアクセスして,操作します。

この項では,これらの関数について簡単に説明します。 これらの関数についての詳細は,リファレンス・ページおよび『Programmer's Guide: STREAMS』を参照してください。

5.2.2.1    open 関数

open 関数は,ストリームをオープンするときに使用します。

関数の構文,パラメータ,エラーについては, open(2) を参照してください。

次の例は,open 関数の使用方法を示しています。

int fd;
fd = open("/dev/streams/echo", O_RDWR);
 

5.2.2.2    close 関数

close 関数は,ストリームをクローズするときに使用します。

関数の構文,パラメータ,エラーについては, close(2) を参照してください。

ストリームに対する最後の close によって,ファイル記述子に関連付けられたストリームが破壊されます。 ストリームの破壊には,ストリーム上のすべてのモジュールのポップ,およびドライバのクローズが含まれます。

5.2.2.3    read 関数

read 関数は,ストリーム・ヘッドで待機中の M_DATA メッセージの内容を受信するときに使用します。

関数の構文,パラメータ,エラーについては, read(2) を参照してください。

read 関数は,M_DATA 以外のメッセージ・タイプでは失敗し,errno に EBADMSG がセットされます。

5.2.2.4    write 関数

write 関数は,データ・バッファから 1 つ以上の M_DATA メッセージを作成するときに使用します。

関数の構文,パラメータ,エラーについては, write(2) を参照してください。

5.2.2.5    ioctl 関数

ioctl 関数は,ストリーム上でさまざまな制御機能を実行するときに使用します。

STREAMS 関数の構文,パラメータ,エラーについては, streamio(7) を参照してください。

次の例は,ioctl システム・コールの使用方法を示しています。

int fd;
fd = open("/dev/streams/echo", O_RDWR, 0);
ioctl(fd,I_PUSH,"pass");
 

5.2.2.6    mkfifo 関数

STREAMS ベースの mkfifo 関数は,単方向の STREAMS ベースのファイル記述子を作成するときに使用します。

注意

libc ライブラリの省略時の mkfifo 関数は STREAMS ベースではありません。 STREAMS ベースの mkfifo 関数を使用するには,アプリケーションを sys5 ライブラリとリンクしなければなりません。 関数の構文,パラメータ,エラーについては, mkfifo(2) を参照してください。

また,mkfifo 関数では,FFM_FS (File on File Mount File System) カーネル・オプションが構成されていなければならないことにも注意してください。 カーネル・オプションの構成についての詳細は,『システム管理ガイド』を参照してください。

5.2.2.7    pipe 関数

STREAMS ベースの pipe 関数は,双方向の STREAMS ベースの通信チャネルを作成するときに使用します。 STREAMS ベースのパイプと STREAMS ベースでないパイプは,次の点が異なります。

注意

libc ライブラリの省略時の pipe 関数は STREAMS ベースではありません。 STREAMS ベースの pipe 関数を使用するには,アプリケーションを sys5 ライブラリとリンクしなければなりません。 関数の構文,パラメータ,エラーについては, pipe(2) を参照してください。

5.2.2.8    putmsg および putpmsg 関数

putmsg および putpmsg 関数は,指定されたバッファからの情報を使用して,STREAMS メッセージ・ブロックを生成するときに使用します。

関数の構文,パラメータ,エラーについては, putmsg(2) を参照してください。

putpmsg 関数は,優先帯域データをダウンストリームに送信する場合に使用します。

各引数は putmsg 関数の引数と同じ意味です。

関数の構文,パラメータ,エラーについては, putpmsg(2) を参照してください。

5.2.2.9    getmsg および getpmsg 関数

getmsg および getpmsg 関数は,ストリーム・ヘッドの読み取りキューにあるメッセージの内容を検索して,ユーザ指定のバッファにいれるときに使用します。

関数の構文,パラメータ,エラーについては, getmsg(2) を参照してください。

getpmsg 関数は,優先帯域データをストリームから受信する場合に使用します。

引数は getmsg 関数の引数と同じ意味です。

関数の構文,パラメータ,エラーについては, getpmsg(2) を参照してください。

5.2.2.10    poll 関数

poll 関数は,ユーザがデータを送受信できるストリームを識別するときに使用します。

関数の構文,パラメータ,エラーについては, poll(2) を参照してください。

5.2.2.11    isastream 関数

isastream 関数は,ファイル記述子が STREAMS ファイルを参照しているかどうかを判断するときに使用します。

次の例は,isastream 関数を使用して,ソケット・ベースのパイプでなく,STREAMS ベースのパイプをオープンしていることを確認する方法を示しています。

int fds[2];
 
pipe(fds);
if (isastream(fds[0]))
       printf("STREAMS based pipe\n");
else
       printf("Sockets based pipe\n");
 

関数の構文,パラメータ,エラーについては, isastream(3) リファレンス・ページを参照してください。

5.2.2.12    fattach 関数

fattach 関数は,STREAMS ベースのファイル記述子を,ファイル・システムの名前空間にあるオブジェクトにアタッチするときに使用します。

次の例は,fattach 関数を使用して,STREAMS ベースのパイプに名前を付ける方法を示しています。

int fds[2];
 
pipe(fds);
fattach(fd[0], "/tmp/pipe1");
 

注意

fattach 関数を使用するには,FFM_FS カーネル・オプションが構成されている必要があります。 カーネル・オプションの構成については,『システム管理ガイド』を参照してください。

関数の構文,パラメータ,エラーについては, fattach(3) を参照してください。

5.2.2.13    fdetach 関数

fdetach関数は,STREAMS ベースのファイル記述子をファイル名からデタッチするときに使用します。

注意

fdetach 関数を使用するには,FFM_FS (File on File Mount File System) カーネル・オプションが構成されている必要があります。 カーネル・オプションの構成については,『システム管理ガイド』を参照してください。

関数の構文,パラメータ,エラーについては, fdetach(3) を参照してください。

表 5-2 は,STREAMS に関連する情報が記述されているリファレンス・ページについて簡単に説明した一覧です。 詳細については,該当するリファレンス・ページを参照してください。

表 5-2:  STREAMS リファレンス・ページ

リファレンス・ページ 説明
autopush(8) システムの自動的にプッシュされた STREAMS モジュールのデータベースを管理するコマンド。
clone(7) 別の STREAMS ドライバで,未使用の主/副デバイスを見つけてオープンする STREAMS ソフトウェア・ドライバ。
close(2) [脚注 17] 指定したファイル記述子に関連付けられたファイルをクローズする関数。
dlb(7) BSD スタイルのデバイス・ドライバと STREAMS プロトコル・スタック間の通信パスを提供する STREAMS 擬似ドライバ。
fattach(3) STREAMS ベースのファイル記述子をファイル・システムのノードにアタッチするコマンド。
fdetach(8) STREAMS ベースのファイル記述子をファイル名からデタッチするコマンド。
fdetach(3) STREAMS ベースのファイル記述子をファイル名からデタッチする関数。
getmsg(2) getpmsg(2) ストリーム・ヘッドの読み取りキューに入っているメッセージを参照する関数。
ifnet(7) データ・リンク・プロバイダ・インタフェース (DLPI) に合わせて作成された STREAMS ベースのデバイス・ドライバとソケット間のブリッジを提供する STREAMS ベースのモジュール。
isastream(3) ファイル記述子が STREAMS ファイルを参照しているかどうかを判断する関数。
mkfifo(2) 単方向の STREAMS ベースのファイル記述子を作成する関数。
open(2) [脚注 17] ファイルとファイル記述子の間に接続を確立する関数。
pipe(2) 双方向の STREAMS ベースのプロセス間通信チャネルを作成する関数。
poll(2) 1 組のファイル記述子に関連する I/O 状態を報告し,指定された 1 つ以上の条件が真になるまで待機する一般的な機能を提供する関数。
putmsg(2) putpmsg(2) STREAMS メッセージ・ブロックを生成する関数。
read(2) [脚注 17] データをファイルから読み取って指定のバッファにいれる関数。
strace(8) STREAMS ログ・ドライバから STREAMS のイベント・トレース・メッセージを検出するアプリケーション。
strchg(1) ストリームの構成を変更するコマンド。
strclean(8) STREAMS のエラー・ログ・ファイルを削除するコマンド。
strconf(1) ストリームの構成について照会するコマンド。
streamio(7) ストリーム上でさまざまな制御関数を実行するコマンド。
strerr(3) STREAMS ログ・ドライバからエラー・メッセージを受信するデーモン。
strlog(7) STREAMS のエラー・ロギング・デーモンおよびイベント・トレース・デーモンが使用する,ログ・メッセージを追跡するインタフェース。
strsetup(8) 適切な STREAMS 擬似デバイスを作成し,STREAMS モジュールの設定を表示するコマンド。
timod(7) トランスポート・インタフェース (TI) をサポートしているトランスポート・ユーザからの ioctl 呼び出しを,TI をサポートしているトランスポート・プロトコル・プロバイダが使用できるメッセージに変換するモジュール。
tirdwr(7) トランスポート・インタフェース (TI) をサポートしているトランスポート・ユーザに,TI をサポートしているトランスポート・プロトコル・プロバイダへの別のインタフェースを提供するモジュール。
write(2) [脚注 17] データを指定のバッファからファイルへ書き込む関数。

5.3    カーネル・レベル関数

この節では,STREAMS モジュールおよびドライバを作成するカーネル・プログラマが熟知しておかなければならない情報について説明します。 説明する情報は,次のとおりです。

5.3.1    モジュール・データ構造体

モジュールまたはドライバがシステムに組み込まれている場合は, そのモジュールまたはドライバで,読み取りキューと書き込みキュー,および他のモジュール情報を定義しなければなりません。

データ構造体 qinitmodule_info,および streamtab は,すべて <sys/stream.h> ヘッダ・ファイルに記述されており,読み取りキューおよび書き込みキューを定義します。 STREAMS モジュールは,宣言部分で,これらのデータ構造体の内容を指定しておかなければなりません。 例については,付録 Aを参照してください。

モジュールが提供しなければならない外部データ構造体は,streamtab だけです。

次に示す qinit 構造体は,キューのためのインタフェース・ルーチンを定義します。 読み取りキューおよび書き込みキューそれぞれに,構造体のセットがあります。

 struct  qinit {
    int     (*qi_putp)();           /* put routine */
    int     (*qi_srvp)();           /* service routine */
    int     (*qi_qopen)();          /* called on each open */
                                    /* or a push */
    int     (*qi_qclose)();         /* called on last close */
                                    /* or a pop */
    int     (*qi_qadmin)();         /* reserved for future use */
    struct module_info * qi_minfo;  /* information structure */
    struct module_stat * qi_mstat;  /* statistics structure (op-
                                    /* tional) */
};
 

module_info 構造体には,モジュールまたはドライバの ID および制限値が入ります。 次の例を参照してください。

 struct  module_info {
    unsigned short  mi_idnum;       /* module ID number */
    char           *mi_idname;      /* module name */
    long            mi_minpsz;      /* min packet size, for */
                                    /* developer use */
    long            mi_maxpsz;      /* max packet size, for */
                                    /* developer use */
    ulong           mi_hiwat;       /* hi-water mark, for */
                                    /* flow control */
    ulong           mi_lowat;       /* lo-water mark, for */
                                    /* flow control */
};
 

streamtab 構造体は,宣言の最上部に記述します。 これは,モジュールまたはドライバの外部に見えなければならない唯一の部分です。 次の例を参照してください。

 struct streamtab {
    struct qinit    * st_rdinit;    /* defines read QUEUE */
    struct qinit    * st_wrinit;    /* defines write QUEUE */
    struct qinit    * st_muxrinit;  /* for multiplexing drivers only */
    struct qinit    * st_muxwinit;  /* ditto */
};
 

5.3.2    メッセージ・データ構造体

Tru64 UNIX STREAMS メッセージは,1 つ以上のリンクされたメッセージ・ブロックから構成されます。 各データ・ブロックには,次の 3 つの構成要素があります。

ストリーム・ヘッドは,データがアプリケーションからダウンストリームに流れるときに,メッセージ・データ構造体を作成して,その内容を指定します。 ストリーム・エンドは,データが外部通信デバイスから流れてくる場合などのように,データがアップストリームに流れるときに,メッセージ・データ構造体を作成して,その内容を指定します。

mblk_t 構造体および dblk_t 構造体を次に示します。 どちらの構造体も <sys/stream.h> ヘッダ・ファイルに記述されています。


/* message block */
struct  msgb {
        struct msgb *   b_next;         /* next message on queue */
        struct msgb *   b_prev;         /* previous message on queue */
        struct msgb *   b_cont;         /* next message block of message */
        unsigned char * b_rptr;         /* first unread data byte in buffer */
        unsigned char * b_wptr;         /* first unwritten data byte */
        struct datab *  b_datap;        /* data block */
        unsigned char   b_band;         /* message priority */
        unsigned char   b_pad1;
        unsigned short  b_flag;         /* message flags */
        long            b_pad2;
        MSG_KERNEL_FIELDS
};
typedef struct msgb     mblk_t;
 
/* data descriptor */
struct  datab {
        union {
                struct datab    * freep;
                struct free_rtn * frtnp;
        } db_f;
        unsigned char * db_base;        /* first byte of buffer */
        unsigned char * db_lim;         /* last byte+1 of buffer */
        unsigned char   db_ref;         /* count of messages pointing */
                                        /* to block */
        unsigned char   db_type;        /* message type */
        unsigned char   db_iswhat;      /* message status */
        unsigned int    db_size;        /* used internally */
        caddr_t         db_msgaddr;     /* used internally */
        long            db_filler;
};
#define db_freep        db_f.freep
#define db_frtnp        db_f.frtnp
 
typedef struct datab    dblk_t;
 
/* Free return structure for esballoc */
typedef struct free_rtn {
        void    (*free_func)(char *, char *);   /* Routine to free buffer */
        char *  free_arg;                       /* Parameter to free_func */
} frtn_t;
 
 

メッセージが STREAMS キューに入っている場合,そのメッセージは,ポインタ b_next および b_prev によってリンクされたメッセージ・リストの一部になります。 キューに入っている先頭のメッセージは,そのキューの q_next ポインタによって指し示され,キューに入っている最後のメッセージは,そのキューの q_last ポインタによって指し示されます。

5.3.3    ドライバおよびモジュールの STREAMS 処理ルーチン

モジュールまたはドライバは,アプリケーションが要求する処理をストリーム上で行うことができます。 ただし,要求された処理を行うためには,STREAMS モジュールまたはドライバは,STREAMS フレームワークによって動作が指定される特殊ルーチンを提供しなければなりません。 この項では,STREAMS モジュールおよびドライバが提供するルーチン,および処理の種類について説明します。 説明する項目は,次のとおりです。

注意

STREAMS モジュールおよびドライバは,オープン,クローズ,および構成処理を提供しなければなりません。 この項で説明する他の処理は,オプションです。

この項では,XX_routine_name というフォーマットを使用して各ルーチンを説明します。 XX は,ユーザ作成の STREAMS モジュール名またはドライバ名に置き換えてください。 たとえば,ユーザ作成の STREAMS 擬似デバイス・ドライバ echoopen ルーチンは,echo_open になります。

5.3.3.1    オープンおよびクローズの処理

open ルーチンおよび close ルーチンだけが,カーネルの u_area へのアクセスを提供します。 これらのルーチンは,シグナルをキャッチした場合にだけ,スリープすることができます。

オープン処理

モジュールおよびドライバには,open ルーチンがなければなりません。 読み取り側の qinit 構造体,st_rdinit は,qi_qopen フィールドに open ルーチンを定義します。 ドライバの open ルーチンは,アプリケーションがストリームをオープンするときに呼び出されます。 ストリーム・ヘッドは,アプリケーションがモジュールをそのストリームにプッシュすると,モジュール内の open ルーチンを呼び出します。

open ルーチンのフォーマットは,次のとおりです。

 XX_open(q, devp, flag, sflag, credp)
       queue_t *q;   /* pointer to the read queue */
       dev_t *devp;  /* pointer to major/minor number
                        for devices */
       int flag;     /* file flag */
       int sflag;    /* stream open flag */
       cred_t *credp /* pointer to a credentials structure */
 
 

open ルーチンは,STREAMS ドライバまたはモジュールが内部で使用するための,データ構造体を割り当てることができます。 このデータ構造体へのポインタは,通常 queue_t 構造体の q_ptr フィールドに格納されます。 このポインタには,モジュールまたはドライバの他の部分から後でアクセスできます。

クローズ処理

モジュールおよびドライバには,close ルーチンがなければなりません。 読み取り側の qinit 構造体,st_rdinit は,qi_qclose フィールドに close ルーチンを定義します。 ストリームをオープンしたアプリケーションがそのストリームをクローズするときに,ドライバは close ルーチンを呼び出します。 スタックからモジュールをポップするときに,ストリーム・ヘッドはモジュール内の close ルーチンを呼び出します。

close ルーチンのフォーマットは,次のとおりです。

XX_close(q, flag, credp)
        queue_t *q;    /* pointer to read queue */
        int flag;      /* file flag */
        cred_t *credp  /* pointer to credentials structure */
 

close ルーチンは,内部で使用したデータ構造体を解放して,クリーン・アップするとよい場合があります。

5.3.3.2    構成処理

configure ルーチンは,STREAMS モジュールまたはドライバをカーネルに組み込むときに使用します。 これは,Tru64 UNIX 固有のものであり,5.4 節で使用方法を説明します。

configure ルーチンのフォーマットは,次のとおりです。

XX_configure(op, indata, indatalen, outdata, outdatalen)
    sysconfig_op_t  op;           /* operation - should be */
                                  /* SYSCONFIG_CONFIGURE */
    str_config_t *  indata;       /* for drivers - describes the device */
    size_t          indatalen;    /* sizeof(str_config_t) */
    str_config_t *  outdata;      /* pointer to returned data */
    size_t          outdatalen;   /* sizeof(str_config_t) */
 
 

5.3.3.3    読み取り側プットおよび書き込み側プットの処理

読み取り側と書き込み側の両方の,XX_Xput ルーチンがあります。 書き込み側のプット処理用は XX_wput であり,読み取り側のプット処理用は XX_rput です。

書き込み側プット処理

書き込み側のプット・ルーチン,XX_wput は,アップストリーム・モジュールの書き込み側が putnext 呼び出しを発行するときに呼び出されます。 XX_wput ルーチンは,アップストリーム・モジュールから現在のモジュールまたはドライバへ引き渡されるメッセージ用の,唯一のインタフェースです。

XX_wput ルーチンのフォーマットは,次のとおりです。

 XX_wput(q, mp)
       queue_t *q;  /* pointer to write queue */
       mblk_t *mp;  /* message pointer */
 
 

読み取り側プット処理

読み取り側のプット・ルーチン,XX_rput は,ダウンストリーム・モジュールの読み取り側が putnext 呼び出しを発行するときに呼び出されます。 ストリーム・エンドであるドライバにはダウンストリーム・モジュールが存在しないので,読み取り側のプット・ルーチンがありません。 XX_rput ルーチンは,ダウンストリーム・モジュールから現在のモジュールへ引き渡されるメッセージ用の,唯一のインタフェースです。

XX_rput ルーチンのフォーマットは,次のとおりです。

 XX_rput(q, mp)
       queue_t *q;  /* pointer to read queue */
       mblk_t *mp;  /* message pointer */
 

XX_Xput ルーチンは,少なくとも次のうちの 1 つを行わなければなりません。

XX_Xput ルーチンは,大量に処理する場合には,サービス・ルーチンに依頼しなければなりせん。

5.3.3.4    読み取り側サービスおよび書き込み側サービスの処理

XX_Xput ルーチンが,大量の処理を必要とするメッセージを受信した場合は,ただちに処理を行うと,フロー制御の問題が生じる可能性があります。 メッセージをただちに処理する代わりに,XX_rput ルーチンは,(putq システム・コールを使用して) メッセージを読み取り側のメッセージ・キューにいれ,XX_wput ルーチンは,メッセージを書き込み側のキューにいれることができます。 STREAMS モジュールは,これらのキューにメッセージが入っていることを通知し,モジュールの読み取り側または書き込み側のサービス・ルーチンをスケジューリングして,それらのメッセージを処理します。 モジュールの XX_rput ルーチンが putq を呼び出さなければ,モジュールは,読み取り側のサービス・ルーチンを必要としません。 同様に,モジュールの XX_wput ルーチンが putq を呼び出さなければ,モジュールは,書き込み側のサービス・ルーチンを必要としません。

基本的なサービス・ルーチンのコードは,読み取り側も書き込み側も,次のフォーマットです。

      XXXsrv(q)
      queue_t *q;
    {
            mblk_t *mp;
 
            while ((mp = getq(q)) != NULL)
            {
                 /*
                 * If flow control is a  problem, return
                 * the message to the queue
                 */
 
                 if (!(canput(q->q_next))
                       return putbq(q, mp);
                 /*
                  * process message
                  */
 
                    putnext(q, mp);
            }
            return 0;
    }
 

5.3.4    Tru64 UNIX STREAMS の概念

次の STREAMS の概念は,Tru64 UNIX 独自のものです。 この項では,これらの概念と実現方法について説明します。

5.3.4.1    同期

Tru64 UNIX は,複数のカーネル STREAMS スレッドの使用をサポートします。 STREAMS キューおよび関連するデータ構造体への排他的アクセスは保証されていません。 メッセージが同じストリームを同時に上下に移動したり,複数のプロセスが同じストリームを下るメッセージを送信することができます。

データ構造体へ同期をとってアクセスするため,各 STREAMS モジュールまたはドライバは許容できる同期レベルを選択します。 同期レベルは,モジュールまたはドライバで許可されている並列処理のレベルを決定します。 同期レベルは,モジュールまたはドライバの構成ルーチンで定義される streamadm データ構造体の sa.sa_syn_level フィールドで定義されます。 sa.sa_syn_level フィールドは,次のいずれかの値でなければなりません。

SQLVL_QUEUE

キュー・レベル同期。 このレベルは,1 つの実行スレッドがモジュールまたはドライバの書き込みキューの任意のインスタンスにアクセスしているときに,別の実行スレッドがモジュールまたはドライバの読み取りキューの任意のインスタンスにアクセスできます。 キュー・レベル同期は,読み取りキューおよび書き込みキューが共通のデータを共用していない場合に使用できます。 引数 SQLVL_QUEUE は,Tru64 UNIX の STREAMS フレームワークにおいて利用できる最下位レベルの同期化を提供します。

たとえば,読み取りキューおよび書き込みキューの q_ptr フィールドは,同じメモリ位置を指しません。

SQLVL_QUEUEPAIR

キュー・ペア・レベル同期。 一度に 1 つのスレッドだけが,このモジュールまたはドライバの各インスタンスの読み取りキューおよび書き込みキューにアクセスできます。 この同期レベルは,データを処理し,ストリームごとの状態のみを持つほとんどのモジュールおよびドライバに共通です。

たとえば,モジュールのインスタンス内において,読み取りキューおよび書き込みキューの q_ptr フィールドは,同じメモリ位置を指します。 モジュール内には他に共用データはありません。

SQLVL_MODULE

モジュール・レベル同期。 このモジュールまたはドライバ内のすべてのコードはシングル・スレッドです。 モジュールまたはドライバのすべてのインスタンスにアクセスできるのは,1 つの実行スレッドだけです。 たとえば,データは,モジュールまたはドライバのすべてのインスタンスによってアクセスされています。

SQLVL_ELSEWHERE

任意レベル同期。 モジュールまたはドライバは,他の任意のモジュールまたはドライバと同期をとります。 このレベルは,互いにデータをアクセスするモジュールまたはドライバのグループの同期をとるために使用されます。 文字列は,streamadm 構造体の sa.sync_info フィールドにあるこのオプションと一緒に渡されます。 この文字列は,モジュールまたはドライバのセットと関連付けるために使用されます。 共同動作するモジュールまたはドライバ間の規約により,この文字列が決定されます。

たとえば,データを共用する TCP モジュールと IP モジュールのようなネットワーキング・スタックが,文字列 tcp/ip の受け渡しを同意したとします。 この場合には,1 つの実行スレッドだけが,この文字列で同期をとるすべてのモジュールまたはドライバにアクセスすることができます。

SQLVL_GLOBAL

グローバル・レベル同期。 これより低いレベルのモジュールまたはドライバはすべてシングル・スレッドです。 保護の異なる他のレベルを使用しているモジュールまたはドライバがあることに注意してください。 このオプションは主にデバッグで利用されます。

5.3.4.2    タイムアウト

timeout および untimeout へのカーネル・インタフェースは次のとおりです。

timeout(func, arg, ticks);
untimeout(func, arg);
 

ただし,AT&T System V Release 4 の STREAMS とソースの互換性を維持するため,<sys/stream.h> ヘッダ・ファイルでは timeout を次のように再定義して,System V インタフェースにします。

id = timeout(func, arg, ticks);
untimeout(id);
 

変数 idint として定義されています。

STREAMS のモジュールおよびドライバは,System V インタフェースを使用しなければなりません。

5.4    Tru64 UNIX カーネル内へのユーザ作成の STREAMS ベースのモジュールまたはドライバの組み込み

ユーザが作成した STREAMS ドライバまたはモジュールにシステムがアクセスするためには,そのドライバとモジュールをシステムのカーネルに組み込まなければなりません。

STREAMS ドライバまたはモジュールは構成可能なカーネル・サブシステムとして認識されるべきなので,『プログラミング・ガイド』の記述に従ってカーネル・サブシステムの構成を行ってください。

次の説明では,ソース・ファイルが mymodule1.c および mymodule2.c である,STREAMS ベースのモジュール (プッシュ可能モジュール,ハードウェア,擬似デバイス・ドライバ) 例 mymod をカーネルに追加する方法を示します。

  1. モジュール・ソース・ファイル (この例では /sys/streamsm/mymodule.c) 内で構成ルーチンを宣言する。

    このサンプルの mymod_configure は,モジュール用です。 ドライバに使用する場合は,次のようにします。

    1. コメント行 /* driver */ に続く次の行のコメントをはずす。

      /* sa.sa_flags    = STR_IS_DEVICE | STR_SYSV4_OPEN; */
       
      

    2. コメント行 /* module */ に続く次の行をコメントにする。

      sa.sa_flags       = STR_IS_MODULE | STR_SYSV4_OPEN;
       
      

    例 5-1:  サンプル・モジュール

    /*
    *  Sample mymodule.c
    */
    
    .
    .
    .
    #include <sys/sysconfig.h> #include <sys/errno.h>   struct streamtab mymodinfo = { &rinit, &winit };   cfg_subsys_attr_t mymod_attributes[] = { [1] {,0,0,0,0,0,0} /* required last element */ };   int mymod_configure( cfg_op_t op; caddr_t indata; ulong indata_size; caddr_t outdata; ulong outdata_size) { dev_t devno = NODEV; [2] struct streamadm sa; if (op != CFG_OP_CONFIGURE) [3] return EINVAL;   sa.sa_version = OSF_STREAMS_10; /* module */ [4] sa.sa_flags = STR_IS_MODULE | STR_SYSV4_OPEN; /* driver */ /* sa.sa_flags = STR_IS_DEVICE | STR_SYSV4_OPEN; */ sa.sa_ttys = NULL; sa.sa_sync_level = SQLVL_MODULE; [5] sa.sa_sync_info = NULL; strcpy(sa.sa_name, "mymod");   if ((devno = strmod_add(devno, &mymodinfo, &sa)) == NODEV) { return ENODEV; }   return ESUCCESS; }

    1. この例は簡略化されているので,例中のサブルーチンが提供する属性テーブルは空で,サブルーチンに属性が渡されることも予期していません。 モジュールに属性を開発する場合は『プログラミング・ガイド』を参照してください。 [例に戻る]

    2. cdevsw テーブルの最初の仕様可能なスロットは自動的にモジュールに割り当てられます。 特定のデバイス番号を予約する場合は,conf.c プログラムの cdevsw テーブルを調べてから定義してください。 cdevsw テーブルおよびデバイス・ドライバ・エントリの追加についての詳細は『Writing Device Drivers』 を参照してください。 [例に戻る]

    3. このルーチン例では,CFG_OP_CONFIGURE オプションだけがサポートされています。 構成ルーチンの他のオプションについては『プログラミング・ガイド』を参照してください。 [例に戻る]

    4. STR_SYSV4_OPEN は,AT&T System V Release 4 の呼び出しシーケンスを使用して,モジュールまたはデバイスの open および close ルーチンを呼び出すことを指定します。 このビットが指定されていない場合には,AT&T System V Release 3.2 の呼び出しシーケンスが使用されます。 [例に戻る]

    5. sa_sync_level フィールドの他のオプションについては,5.3.4 項を参照してください。 [例に戻る]

  2. モジュールをカーネルと静的にリンクする。

    構成する STREAMS モジュールを動的にロード可能にする場合は『プログラミング・ガイド』のカーネル・サブシステムの構成について参照してください。 構成するモジュールがハードウェア・デバイス・ドライバの場合は『Writing Device Drivers』 を参照してください。

    モジュールをカーネルと静的にリンクする場合は,モジュールのソース・ファイル (mymodule1.c および mymodule2.c) を /sys/streamsm ディレクトリに置き,次の例のように /sys/conf/files ファイルのエントリに各ファイルを追加します。 次の例は,mymodule1.c および mymodule2.c/sys/conf/files ファイルのエントリを示します。

    streamsm/mymodule1.c   optional mymod Notbinary
    streamsm/mymodule2.c   optional mymod Notbinary
     
    

    カーネル構成ファイルに MYMOD オプションを追加します。 省略時のカーネル構成ファイルは /sys/conf/HOSTNAME です。 (HOSTNAME にはシステムの名前を大文字で指定します) たとえばシステム名が TRU64 の場合,構成ファイル /sys/conf/TRU64 に次の行を追加します。

    options  MYMOD
     
    

    ハードウェア・デバイス・ドライバを組み込んでいる場合は,手順 3 を続け,ハードウェア・デバイス・ドライバを組み込んでいない場合には,手順 4 に進みます。

  3. ハードウェア・デバイス・ドライバを組み込んでいる場合は,手順 3a から手順 3d までをすべて実行する。

    ハードウェア・デバイス・ドライバを組み込んでいない場合には,手順 4 に進みます。

    ハードウェア・デバイス・ドライバを組み込んでいる場合は,事前に XXprobe および interrupt ルーチンを定義しておかなければなりません。 probe ルーチンおよび interrupt ルーチンの定義方法についての詳細は,『Writing Device Drivers』 を参照してください。

    1. 次の行を,デバイス・ドライバ構成ファイルの先頭に追加する。

      この例では,デバイス・ドライバ構成ファイルは,/sys/stream/mydriver.c です。

      #include <io/common/devdriver.h>
       
      

    2. コントローラ構造体へのポインタを定義する。

      たとえば,次のようにします。

      struct controller *XXinfo;
       
      

      コントローラ構造体についての詳細は,『Writing Device Drivers』 を参照してください。

    3. driver 構造体を宣言して初期化する。

      たとえば,次のようにします。

       struct driver XXdriver =
      {
              XXprobe, 0, 0, 0, 0, XXstd, 0, 0, "XX", XXinfo
      };
       
      

      driver 構造体についての詳細は,『Writing Device Drivers』 を参照してください。

    4. コントローラ行を,カーネル構成ファイルに追加する。

      省略時のカーネル構成ファイルは /sys/conf/HOSTNAME です。 ここで HOSTNAME はマシンの名前です (大文字で示します)。 たとえば,使用しているシステムの名前が TRU64 の場合,次のような 1 行を /sys/conf/TRU64 構成ファイルに追加します。

      controller XX0 at bus vector XXintr
      

      bus キーワードが取り得る値についての詳細は,『システム管理ガイド』を参照してください。

  4. doconfig コマンドを使用して,このマシン用の新しいカーネルを,再構成,再構築して,ブートする。

    カーネルの再構成についての詳細は, doconfig(8),または『システム管理ガイド』を参照してください。

  5. strsetup -c コマンドを実行して,デバイスが正しく構成されていることを確認する。

    # /usr/sbin/strsetup -c
     
    STREAMS Configuration Information...Wed Jun  2 09:30:11 1994
     
               Name       Type   Major      Minor  Module ID
               ----       ----   -----      -----  ---------
              clone                 32          0
                ptm     device      37          0       7609
                pts     device       6          0       7608
                log     device      36          0         44
               nuls     device      38          0       5001
               echo     device      39          0       5000
                sad     device      40          0         45
               pipe     device      41          0       5304
              kinfo     device      42          0       5020
           xtisoUDP     device      43          0       5010
           xtisoTCP     device      44          0       5010
                dlb     device      49          0       5010
            bufcall     module                             0
              timod     module                          5006
             tirdwr     module                             0
              ifnet     module                          5501
              ldtty     module                          7701
               null     module                          5003
               pass     module                          5003
               errm     module                          5003
              spass     module                          5007
             rspass     module                          5008
            pipemod     module                          5303
            Configured devices = 11, modules = 11
     
     
    

5.5    デバイス特殊ファイル

この節では,STREAMS デバイス特殊ファイルとその作成方法について説明します。 また,clone デバイスの概要についても説明します。

すべての STREAMS ドライバには,システムに作成された文字型特殊ファイルがなければなりません。 これらのファイルは,通常,/dev/streams にあり,インストール時に作成されるか,または /usr/sbin/strsetup ユーティリティを実行することによって作成されます。

STREAMS ドライバには,主デバイス番号が関連付けられています。 これは,ドライバをシステムに組み込むときに決定されます。 STREAMS 以外のドライバには,通常,主デバイスと副デバイス番号の各組み合わせに対して文字型特殊ファイルが定義されています。 次は,/dev/rdisk ディレクトリのエントリの例です。

crw-------   1 root     system      8,   1024 Aug 25 15:38 dsk1a
crw-------   1 root     system      8,   1025 Aug 25 15:38 dsk1b
crw-------   1 root     system      8,   1026 Aug 25 15:38 dsk1c
 

この例では,dsk1a の主デバイス番号は 8 であり,副デバイス番号は 1024 です。 dsk1b は,主デバイス番号が 8 で,副デバイス番号が 1025,dsk1c は,主デバイス番号が 8 で,副デバイス番号が 1026 です。

また,STREAMS ドライバの場合にも,主デバイス番号と副デバイス番号の各組み合わせに対して文字型特殊ファイルを定義できます。 次は,/dev/streams ディレクトリのエントリの例です。

crw-rw-rw-   1 root   system   32,  0 Jul 13 12:00 /dev/streams/echo0
crw-rw-rw-   1 root   system   32,  1 Jul 13 12:00 /dev/streams/echo1
 

この例では,echo0 の主デバイス番号は 32 であり,副デバイス番号は 0 です。 echo1 は,主デバイス番号が 32 で,副デバイス番号が 1 です。

アプリケーションが,デバイスに対して一意のストリームをオープンするためには,そのデバイスの未使用の副デバイスをオープンしなければなりません。 最初のアプリケーションは,/dev/streams/echo0 についてオープンでき,2 番目のアプリケーションは,/dev/streams/echo1 についてオープンできます。 これらの各デバイスは,異なる副デバイス番号を持つので,各アプリケーションは,echo ドライバに対して一意のストリームを獲得します。 この方法では,各デバイス (この場合は echo) について,オープンできる各副デバイス用に 1 つの文字型特殊ファイルが必要です。 また,この方法では,すでに使用されている文字型特殊ファイルをオープンしないようにするために,アプリケーションがどの文字型特殊ファイルをオープンするべきかを判断する必要があります。

clone デバイスは,オープンできる各副デバイスに対して,デバイス特殊ファイルを定義するもう 1 つの方法を提供します。 clone デバイスを使用すると,各ドライバは,文字型特殊ファイルを 1 つだけ必要とします。 また,現在利用できる副デバイスをアプリケーションが判別する必要はなく,その代わりに,clone デバイスの主デバイス番号を使用して,2 番目 (または 3 番目) のデバイスをオープンできます。 副デバイス番号は,オープンされているデバイス (この場合は echo) に関連付けられます。 clone デバイスの主デバイス番号を使用してデバイスをオープンするたびに,STREAMS ドライバは,そのデバイスを一意のストリームとして解釈します。

strsetup コマンドは,/dev/streams ディレクトリにエントリを設定して,clone デバイスを使用できるようにします。 次は,/dev/streams ファイルのエントリの例です。

crw-rw-rw-   1 root   system   32,  18 Jul 13 12:00 /dev/streams/echo
 

この例では,システムによって,clone デバイスに主デバイス番号 32 が割り当てられています。 18 は,echo に関連付けられた主デバイス番号です。 アプリケーションが /dev/streams/echo をオープンすると,clone デバイスは,この呼び出しを遮断して,echo ドライバの代わりに open ルーチンを呼び出します。 さらに,clone は,echo ドライバに,クローン・オープンを行うことを通知します。 echo ドライバがクローン・オープンを認識すると,主デバイス番号 18,および最初に利用できる副デバイス番号を返します。

注意

/usr/sbin/strsetup コマンドが作成する文字型特殊ファイルは,省略時には,主デバイス番号と同様に clone を使用して /dev/streams ディレクトリに作成されます。 クローン・オープンを使用しないか,または異なる名前を使用している STREAMS ドライバをカーネルに組み込む場合は, strsetup.conf(4) に記述されている /etc/strsetup.conf ファイルを変更しなければなりません。

使用しているシステムの clone デバイスの主デバイス番号を調べる場合は,strsetup -c コマンドを実行します。

5.6    エラーとイベントのロギング

STREAMS のエラーとイベントのロギングは,次の事項に関連しています。

エラー・ロガー・デーモン,strerr は,STREAMS のエラー・ロギングおよびイベント・トレース機能に送信されたすべてのエラー・メッセージを,ファイルに記録します。

トレース・ロガー,strace は,STREAMS のエラー・ロギングおよびイベント・トレース機能に送信されたトレース・メッセージを,標準出力に書き込みます。

strclean コマンドを実行すると,strerr デーモンによって生成された古いログ・ファイルをすべて削除することができます。

STREAMS モジュールまたはドライバは,strlog カーネル・インタフェースを通して,エラー・メッセージおよびイベント・トレース・メッセージを,STREAMS のエラー・ロギングおよびイベント・トレース機能に送信できます。 これには,strlog の呼び出しが含まれます。

次の例では,STREAMS ドライバが,オープン・ルーチンの実行中に,STREAMS のエラー・ロギングおよびイベント・トレース機能の両方に,主デバイス番号および副デバイス番号を出力します。

#include <sys/strlog.h>
 
strlog(MY_DRIVER_ID, 0, 0, SL_ERROR 1 SL_TRACE,
       "My driver:  mydriver_open() - major=%d,minor=%d",
	major(dev,minor(dev));
 

また,ユーザ・プロセスも,/dev/streams/log に対してストリームをオープンし,putmsg を呼び出すことによって,STREAMS のエラー・ロギングおよびイベント・トレース機能に,メッセージを送信できます。 ログ・メッセージを strlog に引き渡すために,ユーザ・プロセスには,次のようなコードが含まれていなければなりません。

struct strbuf ctl, dat;
struct log_ctl lc;
char *message = "Last edited by <username> on <date>";
 
ctl_len = ctl.maxlen = sizeof (lc);
ctl.buf = (char *)&lc;
 
dat.len = dat.maxlen = strlen(message);
dat.buf = message;
lc.level = 0;
lc.flags = SL_ERROR|SL_NOTIFY;
 
putmsg (log, &ctl, &dat, 0);