C    動的に構成可能なカーネル・サブシステムの作成

新しいカーネル・サブシステムの作成,あるいは既存のカーネル・サブシステムの変更を行う場合,それらを動的に構成可能なように設定することができます。 付録 C では,以下の情報を提供することにより,動的に構成可能なカーネル・サブシステムの作成方法について説明します。

Tru64 UNIX オペレーティング・システムが動的に構成可能なサブシステムをサポートする以前は,それらのシステムの構成ファイルをシステム管理者が編集することによりカーネル・サブシステムを管理していました。 この方法では,サブシステムの追加/変更,あるいはサブシステム・パラメータの変更の際に,場合によっては複雑で時間のかかる,カーネルの再構築が必要でした。 複数のシステムを管理しているシステム管理者は,それらのシステムの各構成ファイルに対して変更を加え,それぞれのカーネルを再構築する必要がありました。

動的に構成可能なサブシステムのサポートによって,システム管理者は,ファイルの編集やカーネルの再構築をせずにシステム・パラメータの変更やサブシステムのロード/アンロードを行うことができます。 システム管理者は,sysconfig コマンドを使用することによりカーネルのサブシステムを構成することができます。 このコマンドを使用することにより,システム管理者は,サブシステムのロードおよび構成,アンロードおよび構成除外,再構成 (変更),および照会をローカル・システムあるいはリモート・システムで行なうことができます。

ロード可能なデバイス・ドライバを作成する場合は,デバイス・ドライバ固有の問題について考慮する必要があります。 ロード可能なデバイス・ドライバの作成方法については,『Writing Device Drivers』を参照してください。

C.1    動的に構成可能なサブシステムの概要

多くの Tru64 UNIX カーネル・サブシステムはスタティック・サブシステムであるため,構築時にカーネルとリンクされています。 カーネルを構築した後は,これらのサブシステムをロードあるいはアンロードすることはできません。 スタティック・サブシステムの例としては,vm (virtual memory: 仮想メモリ) サブシステムが挙げられます。 このサブシステムはシステムが正く動作するために必要なものです。

カーネル・サブシステムによってはロード可能なものもあります。 ロード可能なサブシステムは,カーネルを再構築せずにカーネルに対してサブシステムを追加/削除することができます。 ロード可能なサブシステムの例としては,presto サブシステムが挙げられます。 presto サブシステムは,Prestoserve ソフトウェアを使用している場合のみロードされます。

スタティック・サブシステムおよびロード可能なサブシステムのどちらも,動的に構成することができます。

従来のカーネル・サブシステムと同じように,動的に構成可能なサブシステムはパラメータを持っています。 これらのパラメータは属性と呼ばれます。 サブシステムの属性の例としては,タイムアウト値,テーブル・サイズ,メモリ位置,サブシステム名などがあります。 サブシステムの属性は属性テーブルに定義します。 属性テーブルについてはC.2 節を参照してください。

ロード可能サブシステムを最初に構成する前に,システム管理者は属性に対する値を sysconfigtab データベースに保管することができます。 このデータベースは /etc/sysconfigtab ファイルに保管され,ブート時にカーネル・メモリにロードされます。 このデータベースに保管された値は,サブシステムがその属性の初期値を供給しているかどうかにかかわらず,サブシステムの属性の初期値になります。 図 C-1 は,どのようにして sysconfigtab データベースから初期属性値が得られるかを示しています。

図 C-1:  システム属性値の初期化

図 C-1 で,サブシステムが size 属性の値を 0 (ゼロ) に初期化しているにもかかわらず,size 属性の初期値として sysconfigtab データベースの値が割り当てられていることに注意してください。

サブシステム・コードで宣言されている属性テーブルを使用するには,最初の構成時にどちらのサブシステム属性値を設定できるかを制御します。 sysconfigtab データベースに設定できる属性を制御する方法については,C.2 節を参照してください。

最初の構成時のための属性値を保管できることに加えて,システム管理者は,サブシステムがカーネルに構成されている場合はいつでもその属性値の照会や再構成が可能です。 照会要求に対しては,システム管理者に属性値が返されます。 再構成要求の場合は,属性値は変更されます。 これらの処理は,属性がサブシステム・コードでどのように宣言されているかに依存します。

サブシステムのどの属性値に対する照会あるいは再構成を可能にするかについては,C.2 節に示す方法で制御します。

動的に構成可能な各サブシステムは,属性テーブルに加えて構成ルーチンも使用します。 このルーチンは,サブシステムで保守する属性値の計算などのタスクを行います。 また,たとえばテーブルの大きさの決定,あるいはサブシステムが使用するローカル変数のメモリ位置の保管などのサブシステム固有のタスクも実行します。 C.3 節で,構成ルーチンの作成方法について説明します。 カーネルは,サブシステムが構成,照会,再構成,あるいは構成除外されるたびにサブシステムの構成ファイルを呼び出します。

カーネルに構成できるサブシステムは,カーネル構成から除外することもできます。 システム管理者がカーネル構成からサブシステムを除外すると,そのサブシステムに占有されていたカーネル・メモリは,サブシステムがロード可能な場合には解放されます。 構成除外要求の際にカーネルがサブシステム構成ルーチンを呼び出すことにより,サブシステムはサブシステム固有の構成除外タスクを実行することができます。 サブシステム固有の構成除外タスクの例としては,サブシステム・コードによって割り当てられたメモリの解放があります。

C.2    属性テーブルの概要

動的に構成可能な効果的なサブシステムを作成するポイントは,効果的な属性テーブルを定義することです。 属性テーブルはサブシステムの属性を定義します。 属性の例としては,タイムアウト値,テーブル・サイズ,メモリ位置などがあります。 属性テーブルは,定義属性テーブルと通信属性テーブルの 2 つのフォーマットで存在します。

2 つのタイプの属性テーブルを使用するのは,カーネル・メモリを節約するためです。 定義属性テーブルの情報のいくつか (たとえば,属性名,属性のデータ型など) は通信属性テーブルの情報と同じですが,情報の多くはそれぞれのテーブルで異なります。 たとえば,属性定義時には要求は発生しないので,定義属性テーブルには要求の状態は保管しません。 同様に,通信属性テーブルには,各属性に対する要求のサポート・リストは含まれません。 カーネル・メモリを節約するために,各属性テーブルには必要な情報のみが含まれます。

注意

サブシステム属性テーブルに定義されている属性名は,文字列 method で始まっていてはなりません。 この文字列は,ロード可能デバイス・ドライバ・メソッドで使用されるネーミング属性のために予約されています。 デバイス・ドライバ・メソッドについての詳細は,『Writing Device Drivers』を参照してください。

以降の各項で,/sys/include/sys/sysconfig.h における宣言を示しながら,両方のタイプの属性テーブルについて説明します。

C.2.1    定義属性テーブル

定義属性テーブルには,データ型 cfg_subsys_attr_t が含まれています。 これは,/sys/include/sys/sysconfig.h ファイルで次のように宣言されている属性の構造体です。

typedef struct cfg_attr {
        char         name[CFG_ATTR_NAME_SZ]; [1]
        uint         type; [2]
        uint         operation; [3]
        whatever     address; [4]
        uint         min; [5]
        uint         max;
        uint         binlength; [6]
}cfg_subsys_attr_t;

  1. name フィールドに属性の名前が保管されます。 2 文字から CFG_ATTR_NAME_SZ 定数に保管されている値まで長さの英文字を選択します。 CFG_ATTR_NAME_SZ 定数は,/sys/include/sys/sysconfig.h ファイルで定義されます。 [例に戻る]

  2. このフィールドに,表 C-1 に示すいずれか 1 つの属性データ型を指定します。

    表 C-1:  属性データ型

    データ型名 説明
    CFG_ATTR_STRTYPE ヌルで終わる文字の配列 (char*)
    CFG_ATTR_INTTYPE 32 ビットの符号付きの数値 (int)
    CFG_ATTR_UINTTYPE 32 ビットの符号なしの数値 (unsigned)
    CFG_ATTR_LONGTYPE 64 ビットの符号付きの数値 (long)
    CFG_ATTR_ULONGTYPE 64 ビットの符号なしの数値
    CFG_ATTR_BINTYPE バイトの配列

    [例に戻る]

  3. operation フィールドには,その属性に対して実行できる要求を指定します。 表 C-2 に示す要求コードから 1 つあるいは複数を指定します。

    単一の属性の構成除外はできないため,CFG_OP_UNCONFIGURE 要求コードは,個々の属性に対しては意味を持ちません。

    したがって,operation フィールドに CFG_OP_UNCONFIGURE は指定できません。

    表 C-2:  属性に対して許可される要求を指定するコード

    要求コード 意味
    CFG_OP_CONFIGURE サブシステムが最初に構成されるときに属性の値を設定できる。
    CFG_OP_QUERY サブシステムが構成されていれば,いつでも属性の値を表示できる。
    CFG_OP_RECONFIGURE サブシステムが構成されていれば,いつでも属性の値を変更できる。

    [例に戻る]

  4. address フィールドは,カーネルが属性の値にアクセスするかどうかを決定します。

    このフィールドにアドレスを指定すると,カーネルは属性値の読み取りおよび変更を行うことができます。 sysconfig コマンドからカーネルが照会要求を受信すると,カーネルはこのフィールドに指定されている位置から値を読み取り,その値を返します。 構成要求あるいは再構成要求に対して,カーネルは次の条件をチェックします。

    その値がこれらの条件に当てはまる場合,カーネルは新しい値を属性値として保管します (最大値と最小値は属性定義の次の 2 つのフィールドで指定します)。

    サブシステム・コードの属性に対する照会,構成,再構成などの要求に対してユーザが応答することもできます。 この場合,このフィールドに NULL を指定します。 属性値の制御方法についての詳細は,C.3 節を参照してください。 [例に戻る]

  5. min および max フィールドは,属性値の最小値と最大値を定義します。

    カーネルは,これらの 2 つのフィールドを属性のデータ型に従って別々に解釈します。 属性のデータ型が整数型である場合,これらのフィールドには最小値および最大値を示す整数値が含まれます。 属性のデータ型が CFG_ATTR_STRTYPE である場合,これらのフィールドには文字列の最小長および最大長が含まれます。 属性のデータ型が CFG_ATTR_BINTYPE である場合は,これらのフィールドには,ユーザが修正することのできる最小バイト数および最大バイト数が含まれます。 [例に戻る]

  6. カーネルがバイナリ属性の内容を読み取ったり変更したりできるように設定するには,binlength フィールドを使用してそのバイナリ・データの現在の値を指定します。 その属性の保管されているバイナリ・データの長さをカーネルが変更すると,カーネルはこのフィールドの内容も修正します。

    属性のデータ型が整数あるいは文字列であるか,バイナリ属性に対する照会要求および再構成要求に対して構成ルーチンで応答したい場合には,このフィールドは使用しません。 [例に戻る]

C.2.2    定義属性テーブルの例

例 C-1 に示すのは,定義属性テーブルの例です。 この属性テーブルは,架空のカーネル・サブシステム table_mgr のものです。 この架空のサブシステムの構成ルーチンについては,C.3 節で説明しています。

例 C-1:  定義属性テーブルの例

#include <sys/sysconfig.h>
#include <sys/errno.h>
 
/*
 *   Initialize attributes
 */
static char                  name[] = "Default Table";
static int                   size = 0;
static long                  *table = NULL;
 
 
/*
 *  Declare attributes in an attribute table
 */
 
cfg_subsys_attr_t table_mgr_attrbutes[] = {
  /*
   * "name" is the name of the table
   */
  {"name", [1]                CFG_ATTR_STRTYPE,  [2]
   CFG_OP_CONFIGURE | CFG_OP_QUERY | CFG_OP_RECONFIGURE, [3]
   (caddr_t) name, [4] 2, sizeof(name), [5] 0 [6] },
  /*
   * "size" indicates how large the table should be
   */
  {"size",                        CFG_ATTR_INTTYPE,
   CFG_OP_CONFIGURE | CFG_OP_QUERY | CFG_OP_RECONFIGURE,
   NULL, 1, 10, 0},
  /*
   * "table" is a binary representation of the table
   */
  {"table",                       CFG_ATTR_BINTYPE,
   CFG_OP_QUERY,
   NULL, 0, 0, 0},
  /*
   * "element" is a cell in the table array
   */
  {"element",                     CFG_ATTR_LONGTYPE,
   CFG_OP_QUERY | CFG_OP_RECONFIGURE,
   NULL, 0, 99, 0},
  {,0,0,0,0,0,0}   /* required last element */
};

このテーブルの最後のエントリ {,0,0,0,0,0,0} は空の属性です。 この属性は属性テーブルの終わりを示すもので,すべての属性テーブルで必要なものです。

この属性テーブルの最初の行は,テーブルの名前を定義しています。 この属性テーブルの名前は table_mgr_attributes です。 name 属性のフィールドについて以下に説明します。

  1. 属性名は name フィールドに保管されます。 name は,属性テーブルの最初にあるデータ宣言によって "Default Table" に初期化されています。 [例に戻る]

  2. 属性データ型は CFG_ATTR_STRTYPE です。 これはヌルで終了する文字列配列です。 [例に戻る]

  3. このフィールドは,対象となる属性に対して実行できる操作を指定します。 この例の場合,この属性に対して構成,照会,再構成が可能です。 [例に戻る]

  4. このフィールドは,カーネルがユーザの属性値にアクセスできるかどうかを決定します。

    この例のように,このフィールドにアドレスを指定した場合,カーネルはユーザの属性値の読み取りおよび変更を行うことが可能です。 カーネルが sysconfig コマンドから照会要求を受け取った場合,このフィールドに指定した場所の値を読み取り,その値を返します。 構成要求および再構成要求に対しては,カーネルは,新しい値のデータ型がその属性に対して適当かどうかを確認しその値が属性の最小値と最大値の範囲内に収まるかどうかをチェックします。 この値がこれらの要件に該当する場合,カーネルはその属性に対する新しい値を保管します (最小値および最大値は,属性定義の次の 2 つのフィールドで指定します)。 [例に戻る]

  5. これらの 2 つのフィールドは,属性に対する最小値 (この例の場合 2) および最大値 (この例の場合 sizeof(name)) を定義します。

    属性の最小値および最大値として,システムで定義されている値を使用したい場合は,/usr/include/limits.h ファイルで定義されている定数の 1 つを使用することができます。 [例に戻る]

  6. バイナリ属性の内容をカーネルが読み取り/変更できるように設定するには,このフィールドを使用してバイナリ・データの現在のサイズを指定します。 この属性に保管されているバイナリ・データの長さをカーネルが変更すると,このフィールドの内容も変更されます。

    属性が整数または文字列であるか,バイナリ属性に対する照会あるいは再構成要求に構成ルーチンで応答する場合には,このフィールドは使用されません。 [例に戻る]

C.2.3    通信属性テーブル

/sys/include/sys/sysconfig.h ファイルで定義される通信属性テーブルは,cfg_attr_t データ型を持ちます。 例 C-2 で示すように,このデータ型は属性の構造体になっています。

例 C-2:  通信属性テーブル

typedef struct cfg_attr {
        char         name[CFG_ATTR_NAME_SZ]; [1]
        uint         type; [2]
        uint         status; [3]
        uint         operation; [4]
        long         index; [5]
        union { [6]
          struct {
                   caddr_t val;
                   ulong   min_len;
                   ulong   max_len;
                   void    (*disposal)();
                 }str;
          struct {
                   caddr_t val;
                   ulong   min_size;
                   ulong   max_size;
                   void    (*disposal)();
                   ulong   val_size;
                 }bin;
          struct {
                   caddr_t val;
                   ulong   min_len;
                   ulong   max_len;
                 }num;
               }attr;
}cfg_attr_t;

  1. name フィールドには属性の名前を指定します。 属性名の命名規則は,定義属性テーブルの name フィールドの命名規則と同じです。 [例に戻る]

  2. type フィールドには属性データ型を指定します。 表 C-1 に指定できるデータ型を示します。 [例に戻る]

  3. status フィールドには,表 C-3 に示す定義済み状態コードを指定します。

    表 C-3:  属性状態コード

    状態コード 意味
    CFG_ATTR_EEXISTS 属性が存在しない。
    CFG_ATTR_EINDEX 属性インデックスが正しくない。
    CFG_ATTR_ELARGE 属性値あるいはサイズが大きすぎる。
    CFG_ATTR_EMEM その属性に対して有効なメモリがない。
    CFG_ATTR_EOP その属性は,要求された操作をサポートしていない。
    CFG_ATTR_ESMALL 属性値あるいはサイズが小さすぎる。
    CFG_ATTR_ESUBSYS カーネルによるサブシステムの構成/再構成およびサブシステムに対する照会ヘの応答が許可されない。 サブシステム・コードで応答を行なう必要がある。
    CFG_ATTR_ETYPE 属性タイプが正しくない。 あるいは,属性タイプが一致しない。
    CFG_ATTR_SUCCESS 操作が正しく行なわれた。

    [例に戻る]

  4. operation フィールドには,表 C-2 に示すコードを指定します。 [例に戻る]

  5. index フィールドには,構造化された属性に対するインデックスを指定します。 [例に戻る]

  6. 共用体 attr には,属性値とその最小値および最大値が含まれます。

    CFG_ATTR_STRTYPE データ型の属性の場合,val 変数には文字列データが含まれます。 最小値および最大値は,文字列長の最小値および最大値となります。 disposal ルーチンは,アプリケーションによるカーネル・メモリの使用が終ったら,ユーザがカーネル・メモリに自由に書き込むことができるルーチンです。

    CFG_ATTR_BINTYPE データ型の属性の場合,val フィールドにはバイナリ値が含まれます。 最小値および最大値は,ユーザが変更できるバイト数の最小値および最大値となります。 disposal ルーチンは,アプリケーションによるカーネル・メモリの使用が終ったら,ユーザがカーネル・メモリに自由に書き込むことができるルーチンです。 val_size 変数にはバイナリ・データの現在のサイズが含まれます。

    数値データ型の場合,val 変数には整数値が含まれ,最小値および最大値も整数値となります。 [例に戻る]

C.2.4    通信属性テーブルの例

この項では,通信属性テーブルの例を示します。 この属性テーブルは,架空のカーネル・サブシステム table_mgr のものです。 この架空のサブシステムの構成ルーチンについては,C.3 節で説明しています。

table_mgr_configure(
 cfg_op_t           op,               /*Operation code*/ [1]
 caddr_t            indata,           /*Data passed to the subsystem*/ [2]
 ulong              indata_size,      /*Size of indata*/
 caddr_t            outdata,          /*Data returned to kernel*/ [3]
 ulong              outdata_size)     /*Count of return data items*/
 
{

次の示すのは,table_mgr_configure 通信属性テーブルのフィールドの説明です。

  1. op 変数には,次のいずれかが含まれます。

    CFG_OP_CONFIGURE
    CFG_OP_QUERY
    CFG_OP_RECONFIGURE
    CFG_OP_UNCONFIGURE

    [例に戻る]

  2. indata 構造体は,構成ルーチンに対して indata_size のデータを提供します。 オペレーション・コードが CFG_OP_CONFIGURE あるいは CFG_OP_QUERY の場合,このデータは,構成あるいは照会される属性名のリストです。 オペレーション・コードが CFG_OP_RECONFIGURE の場合,データは属性名と値で構成されます。 オペレーション・コードが CFG_OP_UNCONFIGURE の場合には,構成ルーチンに値は渡されません。 [例に戻る]

  3. outdata 構造体および outdata_size 変数は,構成サブシステムの将来の拡張機能のためのプレースホルダです。 [例に戻る]

C.3    構成ルーチンの作成

サブシステムを構成可能にするためには,構成ルーチンを定義する必要があります。 このルーチンは定義属性テーブルとともに機能し,サブシステムの構成/再構成,サブシステムの照会に対する応答,サブシステムの構成除外を行ないます。

サブシステムでの必要性に依存して,構成ルーチンは単純であったり複雑であったりします。 構成ルーチンの目的は,カーネルが実行できないタスクを実行することです。 定義属性テーブルによって属性のロケーションをカーネルに知らせることができるので,カーネルは属性に関するすべての構成要求,再構成要求,照会要求を処理することができます。 ただし,これらの要求が発生している間,カーネルにおける処理量は制限されます。 たとえば,カーネルによる属性値の計算などは行えないので,属性値を計算する必要があるような属性については構成ファイルで処理する必要があります。

以降の各項で,構成ルーチンの例を示します。 ここで示すサンプル・ルーチンは,バイナリ・データを保守するための架空のサブシステム table_mgr のための構成ルーチンです。 この構成ルーチンは,次のタスクを行います。

このサブシステムのソース・コードは,/usr/examples/cfgmgr ディレクトリに含まれています。 このサブシステムの定義属性テーブルについてはC.2.2 項を,通信属性テーブルについてはC.2.4 項を参照してください。

C.3.1    初期構成の実行

最初の構成時に,table_mgr サブシステムは,それが保守するテーブルを作成します。 例 C-1 に示すように,システム管理者は,初期構成時にテーブルの名前とサイズを設定することができます。 これらの値を設定するために,システム管理者は,sysconfigtab データベースに希望する値を保管しておきます。

サブシステムのコードで定義されているこのテーブルの省略時の名前は,Default  Table です。 また,このテーブルの省略時のサイズはゼロです。

例 C-3 に示すのは,table_mgr サブシステムの初期構成時に実行されるコードです。

例 C-3:  初期構成の実行


.
.
.
switch(op){ [1] case CFG_OP_CONFIGURE:   attributes = (cfg_attr_t*)indata; [2]   for (i=0; i<indata_size; i++){ [3] if (attributes[i].status == CFG_ATTR_ESUBSYS) { [4]   if (!strcmp("size", attributes[i].name)){ [5] /* Set the size of the table */ table = (long *) kalloc(attributes[i].attr.num.val*sizeof(long)); [6]   /* * Make sure that memory is available */   if (table == NULL) { [7] attributes[i].status = CFG_ATTR_EMEM; continue; }   /* * Success, so update the new table size and attribute status */   size = attributes[i].attr.num.val; [8] attributes[i].status = CFG_ATTR_SUCCESS; continue; } } } break;
.
.
.

  1. 構成ルーチンには switch 文が含まれます。 この switch 文によって,サブシステムは多くの操作に対して応答することができます。 サブシステムは op 変数の値に従って,異なるタスクを実行します。 [例に戻る]

  2. この文は,attributes ポインタを初期化します。 これにより,indata 構造体で渡されたデータを構成ルーチンが操作できるようになります。 [例に戻る]

  3. for ループは,構成ルーチンに渡される各属性の状態を確認します。 [例に戻る]

  4. 属性の状態フィールドに CFG_ATTR_ESUBSYS が含まれている場合,構成ルーチンはその属性を構成しなければなりません。 [例に戻る]

  5. 最初の構成時に操作する必要のある唯一の属性は size です。 size 属性が現在の属性である場合のみ,if 文内のコードが実行されます。 [例に戻る]

  6. 状態フィールドに CFG_ATTR_ESUBSYS が含まれ,属性名フィールドに size が含まれる場合,ローカル変数 table はカーネル・メモリ領域のアドレスを受け取ります。 カーネル・メモリ領域は,attributes[i].attr.num.val で指定されているテーブルのサイズを保管するのに十分な大きさでなければなりません。 attributes[i].attr.num.val に指定される値は,テーブル内のロングワード数を指定する整数です。 カーネルは sysconfigtab データベースから整数値を読み取り,attr 共用体で構成ルーチンに渡します。 [例に戻る]

  7. カーネル・メモリを割り当てることができない場合,kalloc ルーチンは NULL を返します。 テーブルに対してメモリが割り当てられていない場合,構成ルーチンは,メモリを使用できないことを示す CFG_ATTR_EMEM を返します。 この場合,カーネルはエラー・メッセージを表示します。 サブシステムはカーネルに構成されますが,システム管理者は,sysconfig コマンドを使用してテーブルのサイズを再設定する必要があります。 [例に戻る]

  8. カーネル・メモリが正しく割り当てられると,sysconfigtab ファイルのテーブル・サイズが静的な外部変数 size に保管されます。 これによりサブシステムは,テーブルのサイズを必要とする操作に対してこの値を使用することができるようになります。 [例に戻る]

C.3.2    照会要求に対する応答

table_mgr サブシステムのユーザは,照会要求によって次の項目を表示するよう要求できます。

例 C-1 に示すように,name 属性の宣言には,カーネルがそのテーブルの名前に直接アクセスできるようにアドレス (caddr_t)  name が含まれています。 このため,構成ルーチンには,テーブル名に関する照会に応答するためのコードは不要です。

例 C-4 に示すのは,照会要求の一部として実行されるコードの例です。

例 C-4:  照会要求に対する応答

 switch (op):

.
.
.
case CFG_OP_QUERY: /* * indata is a list of attributes to be queried, and * indata_size is the count of attributes */ attributes = (cfg_attr_t *) indata; [1]   for (i = 0; i < indata_size; i++) { [2] if (attributes[i].status == CFG_ATTR_ESUBSYS) { [3]   /* * We need to handle the query for the following * attributes. */   if (!strcmp(attributes[i].name, "size")) { [4]   /* * Fetch the size of the table. */ attributes[i].attr.num.val = (long) size; attributes[i].status = CFG_ATTR_SUCCESS; continue; }   if (!strcmp(attributes[i].name, "table")) { [5]   /* * Fetch the address of the table, along with its size. */ attributes[i].attr.bin.val = (caddr_t) table; attributes[i].attr.bin.val_size = size * sizeof(long); attributes[i].status = CFG_ATTR_SUCCESS; continue; }   if (!strcmp(attributes[i].name, "element")) { [6]   /* * Make sure that the index is in the right range. */ if (attributes[i].index < 1 || attributes[i].index > size) { attributes[i].status = CFG_ATTR_EINDEX; continue; }   /* * Fetch the element. */ attributes[i].attr.num.val = table[attributes[i].index - 1]; attributes[i].status = CFG_ATTR_SUCCESS; continue; } } }   break;
.
.
.

  1. この文は,attributes ポインタを初期化します。 これにより,indata 構造体で渡されたデータを構成ルーチンが操作できるようになります。 [例に戻る]

  2. for ループは,構成ルーチンに渡される各属性の状態を確認します。 [例に戻る]

  3. 属性の状態フィールドに CFG_ATTR_ESUBSYS が含まれている場合,構成ルーチンはその属性に関する照会に応答しなければなりません。 [例に戻る]

  4. 現在の属性が size の場合,このルーチンは size 変数の値を attr 共用体の val フィールド (attributes[i].attr.num.val) にコピーします。 size 変数の値は整数なので,共用体の num の部分が使用されます。

    次にこのルーチンは,状態フィールド attributes[i].statusCFG_ATTR_SUCCESS を保管します。 [例に戻る]

  5. 現在の属性が table の場合,このルーチンは テーブルのアドレスを attr 共用体の val フィールドに保管します。 この属性はバイナリなので,共用体の bin の部分が使用され,テーブルのサイズは val_size フィールドに保管されます。 テーブルのサイズは,現在のテーブル・サイズ size と,このサイズのロングワードを乗算することにより計算されます。

    status フィールドは CFG_ATTR_SUCCESS に設定されて,処理が成功したことを示します。 [例に戻る]

  6. 現在の属性が element の場合,このルーチンはテーブルの要素の値を attr 共用体の val フィールドに保管します。 各フィールドはロングワードなので,attr 共用体の num の部分が使用されます。

    sysconfig コマンド行で指定したインデックスが範囲外の場合,ルーチンは CFG_ATTR_EINDEX を状態フィールドに保管します。 この場合,カーネルはエラー・メッセージを表示します。 システム管理者は別のインデックスで再処理する必要があります。

    インデックスが範囲内の場合,status フィールドには CFG_ATTR_SUCCESS が設定されて,処理が成功したことを示します。 [例に戻る]

C.3.3    再構成要求に対する応答

再構成要求は,table_mgr サブシステムの属性を変更します。 例 C-1 に示す定義属性テーブルでは,次の table_mgr 属性をシステム管理者が再構成することができます。

例 C-1 に示すように,name 属性宣言には,カーネルがテーブル名に直接アクセスできるようにアドレス ((caddr_t) name) が含まれます。 このため構成ルーチンには,テーブル名の再構成要求に応答するためのコードは不要です。

例 C-5 に示すのは,再構成要求の際に実行されるコードの例です。

例 C-5:  再構成要求に対する応答

 switch(op){

.
.
.
case CFG_OP_RECONFIGURE: /* * The indata parameter is a list of attributes to be * reconfigured, and indata_size is the count of attributes. */ attributes = (cfg_attr_t *) indata; [1]   for (i = 0; i < indata_size; i++) { [2] if (attributes[i].status == CFG_ATTR_ESUBSYS) { [3]   /* * We need to handle the reconfigure for the following * attributes. */   if (!strcmp(attributes[i].name, "size")) { [4]   long *new_table; int new_size;   /* * Change the size of the table. */ new_size = (int) attributes[i].attr.num.val; [5] new_table = (long *) kalloc(new_size * sizeof(long));   /* * Make sure that we were able to allocate memory. */ if (new_table == NULL) { [6] attributes[i].status = CFG_ATTR_EMEM; continue; }   /* * Update the new table with the contents of the old one, * then free the memory for the old table. */ if (size) { [7] bcopy(table, new_table, sizeof(long) * ((size < new_size) ? size : new_size)); kfree(table); }   /* * Success, so update the new table address and size. */ table = new_table; [8] size = new_size; attributes[i].status = CFG_ATTR_SUCCESS; continue; }   if (!strcmp(attributes[i].name, "element")) { [9]   /* * Make sure that the index is in the right range. */ if (attributes[i].index < 1 || attributes[i].index > size) {[10] attributes[i].status = CFG_ATTR_EINDEX; continue; }   /* * Update the element. */ table[attributes[i].index - 1] = attributes[i].attr.num.val; [11] attributes[i].status = CFG_ATTR_SUCCESS; continue; } } }   break;  
.
.
.

  1. この文は,attributes ポインタを初期化します。 これにより,indata 構造体で渡されたデータを構成ルーチンが操作できるようになります。 [例に戻る]

  2. for ループは,構成ルーチンに渡される各属性の状態を確認します。 [例に戻る]

  3. 属性の状態フィールドに CFG_ATTR_ESUBSYS が含まれている場合,構成ルーチンはその属性を再構成しなければなりません。 [例に戻る]

  4. 現在の属性が size の場合,再構成を行なうとテーブルのサイズが変更されます。 サブシステムは,カーネル・メモリを使用することができ,既存のテーブルのデータを損失しないことを保証する必要があるため,2 つの変数が新たに宣言されます。 new_table および new_size 変数は,新しいテーブルの定義と新しいテーブル・サイズを保管します。 [例に戻る]

  5. new_size 変数は,attributes[i].attr.num.val フィールドで渡される新しいサイズを受け取ります。 この値は sysconfig コマンド行で指定されます。

    new_table 変数は,新しいテーブル・サイズとして適切なバイト数を含んでいるメモリ領域をポイントするアドレスを受け取ります。 新しいテーブル・サイズは,new_size 変数の値とバイト数のロングワード (sizeof(long)) の乗算で計算されます。 [例に戻る]

  6. カーネル・メモリを割り当てることができない場合,kalloc ルーチンは NULL を返します。 テーブルに対してメモリが割り当てられていない場合,構成ルーチンは,メモリを使用できないことを示す CFG_ATTR_EMEM を返します。 この場合,カーネルはエラー・メッセージを表示します。 システム管理者は,適切な値を指定して sysconfig コマンドを再入力する必要があります。 [例に戻る]

  7. この if 文は,テーブルが存在するかどうかを判断します。 テーブルが存在する場合,サブシステムは既存テーブルから新しいテーブルへデータをコピーし,既存テーブルが占有していたメモリを解放します。 [例に戻る]

  8. 最後に,カーネル・メモリが割り当てられ,既存テーブルのデータが保管されたことをサブシステムが確認した後,サブシステムは new_table に保管されているアドレスを table に移します。 また,サブシステムは新しいテーブル・サイズを new_size から size へ移します。

    CFG_ATTR_SUCCESSstatus フィールドに設定されて,操作が正しく行われたことを示します。 [例に戻る]

  9. 現在の属性が element の場合,サブシステムは新しいテーブル要素をテーブルに保管します。 [例に戻る]

  10. 値を保管する前に,指定されたインデックスが有効な範囲内にあるかどうかをルーチンが確認します。 インデックスが範囲外の場合,ルーチンは状態フィールドに CFG_ATTR_EINDEX を保管します。 この場合,カーネルはエラー・メッセージを表示します。 システム管理者は,別のインデックスで再操作する必要があります。 [例に戻る]

  11. インデックスが範囲内の場合,サブシステムは,attr 共用体の val フィールドをテーブルの要素に保管します。 各要素はロングワードなので,attr 共用体の num の部分が使用されます。

    操作が正しく行われたことを示す CFG_ATTR_SUCCESSstatus フィールドに設定されます。 [例に戻る]

C.3.4    サブシステムで定義した操作の実行

table_mgr サブシステムは,テーブル内のすべてのフィールドの値を 2 倍にするアプリケーション固有の演算を定義します。

サブシステムで独自の演算を定義する場合,<sys/sysconfig.h> ファイルで定義しているように,演算コードは CFG_OP_SUBSYS_MIN から CFG_OP_SUBSYS_MAX までの範囲でなければなりません。 カーネルは,この範囲の演算コードを受け取ると,直ちにサブシステム・コードに制御を移します。 サブシステム定義の演算に対しては,カーネルは動作しません。

サブシステムに制御が移ると,サブシステムは,要求で渡されたデータの操作を含め,演算を実行します。

次の例は,CFG_OP_SUBSYS_MIN 値を持つ要求に対する応答で実行されたコードです。

  switch (op) {

.
.
.
case CFG_OP_SUBSYS_MIN:   /* * Double each element of the table. */ for (i=0; ((table != NULL) && (i < size)); i++) table[i] *= 2;   break;  
.
.
.
}  

このコードは,テーブル内の各要素の値を 2 倍にします。

C.3.5    サブシステムの構成除外

table_mgr サブシステムを構成除外すると,カーネル・メモリが解放されます。 次に示すのは,構成除外要求に応答する際に実行されるコード例です。

  switch(op){

.
.
.
case CFG_OP_UNCONFIGURE: /* * Free up the table if we allocated one. */ if (size) kfree(table, size*sizeof(long)); size = 0; break; }   return ESUCCESS; }  

この構成ルーチンのコードは,テーブルに対してメモリが割り当てられているかどうかを判断します。 メモリが割り当てられている場合,ルーチンは kfree 関数を使用してメモリを解放します。

C.3.6    構成ルーチンの終了

次に示すのは,return 文の例です。

  switch(op){

.
.
.
size = 0; break; }   return ESUCCESS;  

構成要求,再構成要求,照会要求,あるいは構成除外要求が完了すると,サブシステム構成ルーチンは ESUCCESS を返します。 このサブシステムが設計された方法では,構成,照会,再構成,あるいは構成除外要求の全体が失敗することはありません。 C.3.1 項およびC.3.3 項に示すように,個々の属性についての操作が失敗することはあります。

たとえば,1 つ以上のキー属性の構成に失敗したときにサブシステムの構成全体を失敗させたいときのように,状況によっては,サブシステムの構成,再構成,あるいは構成除外を失敗させたい場合があるかもしれません。 次の例は,エラー値でルーチンを終了する例を示しています。

  switch(op){

.
.
.
if (table == NULL) { attributes[i].status = CFG_ATTR_EMEM; return ENOMEM; /*Return message from errno.h*/ }  

この例の if 文は,テーブルに対してメモリが割り当てられているかどうかをテストします。 メモリが割り当てられていない場合,サブシステムはエラー状態を返しサブシステムの構成は失敗します。 /sys/include/sys/sysconfig.h および /usr/include/errno.h ファイルで定義されているように,次のメッセージが表示されます。

No memory available for the attribute
Not enough core
 

この場合,システム管理者は,再度 sysconfig コマンドを実行してサブシステムの構成を行う必要があります。

0 以外の戻り値は,エラー状態と解釈されます。 サブシステムがエラー状態を返した場合は,次のような状況が発生します。

C.4    オペレーティング・システムのリビジョンの確認

ロード可能なサブシステムを作成する場合,オペレーティング・システムのバージョン番号を確認するためのコードをサブシステムに追加すべきです。 このコードにより,作成したサブシステムとは互換性のないバージョンのオペレーティング・システムにそのサブシステムがロードされるのを防ぎます。

新たにリリースされたオペレーティング・システムが前のバージョンと比較して大きな違いがある場合,このリリースをメジャー・リリースと呼びます。 メジャー・リリースの場合,オペレーティング・システムに対して行われた変更が原因でサブシステムが正しく動作しないことがあるので,オペレーティング・システムのメジャー・リリースごとにテストを行い,必要ならサブシステムを修正する必要があります。 メジャー・リリースでオペレーティング・システムに新たに追加された機能を使用したい場合も同様です。

新たにリリースされたオペレーティング・システムが,前のバージョンと比較して大きな違いがない場合,このリリースをマイナー・リリースと呼びます。 一般に,メジャー・リリースのオペレーティング・システム上では,サブシステムに何も変更を加えないで実行できるはずです。 ただし,新しいオペレーティング・システムでのテストは行うべきです。 オペレーティング・システムに新たに追加された機能を使用したい場合も同様です。

オペレーティング・システムのバージョン番号を調べたい場合は,Tru64 UNIX システムが提供するグローバル・カーネル変数 version_major および version_minor を使用します。 次に示すのは,オペレーティング・システムのバージョンを調べるためのコード例です。


.
.
.
extern int version_major; extern int version_minor;   if (version_major != 5 && version_minor != 0) return EVERSION;  

この例では,バージョン 5.0 のオペレーティング・システムでサブシステムを実行するように指定しています。

C.5    ロード可能なサブシステムの構築とロード

ロード可能なサブシステムを作成した後,テストのため,それを構築して,カーネルに組み込む必要があります。 この節では,ロード可能なサブシステムの構築およびロード方法について説明します。 実行時の属性構成が可能なスタティック・サブシステムの構築方法については,C.6 節を参照してください。

動的にロード可能なサブシステムを構築するための,次に示すプロシージャでは,ファイル table_mgr.ctable_data.c に含まれている table_mgr という名前のサブシステムを構築しているものとします。 このサブシステムを構築するには,次の手順に従います。

  1. サブシステムのソース・ファイルを /usr/sys 領域のディレクトリに移動します。

    # mkdir /usr/sys/mysubsys
    # cp table_mgr.c /usr/sys/mysubsys/table_mgr.c
    # cp table_data.c /usr/sys/mysubsys/table_data.c
     
    

    ディレクトリ名 mysubsys は,好みのディレクトリ名に置き換えることができます。

  2. 好みのテキスト・エディタを使用して,/usr/sys/conf/files ファイルを編集し,次の行を挿入します。

    #
    # table_mgr subsystem
    #
    MODULE/DYNAMIC/table_mgr        optional table_mgr Binary
    mysubsys/table_mgr.c            module table_mgr
    mysubsys/table_data.c           module table_mgr
     
    

    files ファイルのエントリは,config プログラムに対するサブシステムを記述します。 このエントリの最初の行には,次の情報が入っています。

    files ファイル・エントリに続く行には,各モジュールを構成するソース・ファイルへのパス名を指定します。

  3. 次のコマンドを入力して,makefile と関連するヘッダ・ファイルを生成します。

    # /usr/sys/conf/sourceconfig BINARY
     
    

  4. /usr/sys/BINARY ディレクトリに変更して,次のようにモジュールを構築します。

    # cd /usr/sys/BINARY
    # make table_mgr.mod
     
    

  5. エラーなしでモジュールが構築できると,それを /subsys ディレクトリに移動して,システムがロードできるようにします。

    # cp table_mgr.mod /subsys/
     
    

  6. /sbin/sysconfig コマンドまたは /sbin/init.d/autosysconfig コマンドを使用して,サブシステムをロードします。

    次に示すコマンド行は,table_mgr サブシステムのロードおよび構成に使用できます。

    # /sbin/sysconfig -c table_mgr
     
    

    システムをリブートするたびに,そのサブシステムをカーネルに組み込みたい場合は,次のコマンドを入力します。

    # /sbin/init.d/autosysconfig add table_mgr
     
    

    autosysconfig コマンドは,table_mgr サブシステムを自動的にカーネルに組み込むサブシステムのリストに追加します。

C.6    静的な構成可能サブシステムのカーネルへの構築

実行時に属性構成が可能なスタティック・サブシステムを作成すると,テストのため,それをカーネルに組み込む必要があります。 この節では,属性の動的構成をサポートするスタティック・サブシステムの構築方法について説明します。

動的にロード可能なサブシステムを構築するための,次に示すプロシージャでは,ファイル table_mgr.c に格納されている table_mgr という名前のサブシステムを構築しているものとします。

  1. サブシステムのソース・ファイルを /usr/sys 領域のディレクトリに移動します。

    # mkdir /usr/sys/mysubsys
    # cp table_mgr.c /usr/sys/mysubsys/table_mgr.c
    # cp table_data.c /usr/sys/mysubsys/table_data.c
     
    

    ディレクトリ名 mysubsys は,好みのディレクトリ名に置き換えることができます。

  2. 好みのテキスト・エディタを使用して,/usr/sys/conf/files ファイルを編集し,次の行を挿入します。

    #
    # table_mgr subsystem
    #
    MODULE/STATIC/table_mgr         optional table_mgr Binary
    mysubsys/table_mgr.c            module table_mgr
    mysubsys/table_data.c           module table_mgr
     
    

    files ファイルのエントリは,config プログラムに対するサブシステムを記述します。 このエントリの最初の行には,次の情報が入っています。

    files ファイル・エントリに続く行には,各モジュールを構成するソース・ファイルへのパス名を指定します。

  3. /usr/sbin/doconfig プログラムを実行して,カーネルを再構築します。

    # /usr/sbin/doconfig
     
    

  4. 次のプロンプトに対して,構成ファイルの名前を入力します。

        *** KERNEL CONFIGURATION AND BUILD PROCEDURE ***
    Enter a name for the kernel configuration file. [MYSYS]: MYSYS.TEST
     
    

    カーネル・サブシステムをテストするため,構成ファイルの新しいファイル名を入力します。 たとえば,MYSYS.TEST と入力します。 doconfig プログラムで新しい構成ファイル名を指定すると,システム上の既存の構成ファイルは変更されません。 このようにしておくと,後で既存の構成ファイルを使用して,テスト中のサブシステムを除いて,システムを構成することができます。

  5. Kernel Option Selection メニューからオプション 15 を選択します。 オプション 15 は,新しいカーネル・オプションを追加していることを示します。

  6. 次のプロンプトに対して応答すると,構成ファイルを編集することを示します。

    Do you want to edit the configuration file? (y/n) [n] yes
    

    すると,doconfig プログラムはエディタを起動します (doconfig で起動するエディタを制御するには,EDITOR 環境変数を定義します)。 サブシステムの識別子 (この場合には table_mgr) を構成ファイルに追加します。

    options   TABLE_MGR
     
    

    エディタを終了すると,doconfig プログラムが新しい構成ファイルと新しいカーネルを構築します。

  7. 新しいカーネルをルート (/) ディレクトリにコピーします。

    # cp /usr/sys/MYSYS_TEST/vmunix /vmunix
     
    

  8. システムをシャットダウンしてリブートします。

    # shutdown -r now
     
    

注意

optional キーワードを standard に置き換えると,カーネルでそのモジュールが必須であることを指定できます。 standard キーワードを使用すると,システム構成ファイルを編集する手間を省くことができます。 次に示す files ファイル・エントリは,必須カーネル・モジュールのためのものであり,システム構成ファイルに含まれているかどうかに関係なく,カーネルに組み込まれます。

#
# table_mgr subsystem
#
MODULE/STATIC/table_mgr         standard Binary
mysubsys/table_mgr.c            module table_mgr
mysubsys/table_data.c           module table_mgr

前述のようなエントリを files ファイルに記述する場合には,MYSYS: という名前のシステム上で,次のコマンドを入力することにより,サブシステムをカーネルに追加します。

# /usr/sbin/doconfig -c MYSYS
 

このコマンドの MYSYS は,使用しているシステム構成ファイル名と置き換えてください。

このコマンドを実行すると,テストしているサブシステム,この場合には table_mgr サブシステムを加えて,既存のシステム構成ファイルに記述されている vmunix カーネルを構築します。

C.7    サブシステムのテスト

サブシステムにおける構成,照会,再構成,構成除外要求のテストは,sysconfig コマンドを使用して行うことができます。 サブシステムをテストしている場合は,-v オプションを指定して sysconfig コマンドを入力します。 -v オプションを指定すると,sysconfig コマンドは通常より多くの情報を表示します。 このコマンドは,cfgmgr 構成管理サーバと kloadsrv カーネル・ローディング・ソフトウェアからの情報を /dev/console スクリーンに表示します。 kloadsrv からの情報は,サブシステムのロードが失敗する原因となる未解決シンボルの名前を調べるのに特に便利です。

多くの場合,カーネル・サブシステムのデバッグには,他のカーネル・プログラムをテストするのと同じように dbxkdebug,および kdbx を使用することができます。 dbx -remote コマンドを使用して kdebug デバッガを使用する場合,サブシステムの .mod ファイルは,dbx を実行しているシステムとリモート・テスト・システムで同じ位置になければなりません。 サブシステムのソース・コードは,dbx を実行しているシステム上の同じ位置になければなりません。 kdebug デバッガを使用するために必要な設定については,『Kernel Debugging』を参照してください。

dbx 起動時に動的にロード可能なサブシステムがロードされていない場合は,dbx addobj コマンドを実行することによりデバッガがサブシステムの開始アドレスを決定することができます。 サブシステムの開始アドレスへデバッガがアクセスできない場合は,サブシステムのデータを調べサブシステムのコードにブレークポイントを設定するために,dbx addobj コマンドを使用することはできません。 次の手順は,dbx デバッガを起動して,table_mgr.mod サブシステムを構成し,addobj コマンドを入力する方法を示しています。

  1. dbx デバッガを起動します。

    # dbx -k /vmunix
    dbx version 3.11.4
    Type 'help' for help.
     
    stopped at [thread_block:1542 ,0xfffffc00002f5334]
     
    (dbx)
     
    

  2. sysconfig コマンドを入力して,サブシステムの最初の構成を行います。

    # sysconfig -c table_mgr
     
    

  3. 次のように addobj コマンドを入力します。

    (dbx) addobj /subsys/table_mgr.mod
    (dbx) p &table_mgr_configure
    0xffffffff895aa000
     
    

    addobj コマンドにはサブシステムの絶対パス名を指定してください (dbx セッションを開始する前にサブシステムがロードされている場合は,addobj コマンドを実行する必要はありません)。

初めて構成するサブシステムのサブシステム・コードにブレークポイントを設定するには,サブシステムのロードの後,カーネルが構成ルーチンを呼び出す前に,addobj コマンドを入力します。 サブシステムのロードと構成ファイルの呼び出しの間で実行を停止するには,特別なルーチン subsys_preconfigure にブレークポイントを設定します。 次の手順は,このブレークポイントの設定方法を示しています。

  1. dbx デバッガを起動して,subsys_preconfigure ルーチンにブレークポイントを設定します。 次のように入力してください。

    # dbx -remote /vmunix
    dbx version 3.11.4
    Type 'help' for help.
     
    stopped at [thread_block:1542 ,0xfffffc00002f5334]
     
    (dbx) stop in subsys_preconfigure
    (dbx) run
    

  2. sysconfig コマンドを入力して table_mgr サブシステムを初期構成します。

    # sysconfig -c table_mgr
     
    

  3. addobj コマンドを入力して,構成ルーチンにブレークポイントを設定します。

    [5] stopped at   [subsys_preconfigure:1546 ,0xfffffc0000273c58]
    (dbx) addobj /subsys/table_mgr.mod
    (dbx) stop in table_mgr_configure
    [6] stop in table_mgr_configure
    (dbx) continue
    [6] stopped at   [table_mgr_configure:47 ,0xffffffff895aa028]
    (dbx) 
     
    

  4. subsys_preconfigure ルーチンで実行が停止したら dbx スタック・トレース・コマンド trace を実行することにより,構成要求がテスト中のサブシステムに対するものであることを確認できます。 次に,サブシステム構成ルーチンにブレークポイントを設定します。