UNIX の デミイスドライバを 解剖する デバイスドライバのコードは , その構造は普通の コードと同じてす。 UNIX て、はドライバは C て、書くの が普通て、す ( 訳注 7 ) 典型的なデバイスドライバて、は , まず冒頭部て、い くつかのシステムヘッダファイルを #include し , そ してデバイスのレジスタのアドレスや内容を # define します。また , そのドライバのグローバル変数も宣 言します。 UNIX のデバイスドライバのほとんどて、 # include されるファイルは以下のとおりて、す。 : バッフアのヘッダ部の情報の定義 : 端末と clist 構造体関連の定義 sys/user. h : user 領域の定義 ( 訳注 8 ) の定義 : ディレクトリ構造に関するパラメータ カーネルのノヾラメータの定義 sys/buf. h sys/tty. h sys/di r. h / * 受信レジスタ * / #define RRDATA (UART BASE 十 0 ) / * 架空のメモリマッブド I/O アドレス * / #define UART BASE Oxffffff3a / * 架空の UART のレジスタアドレスの定義 * / ジスタを次のように #define するてしよう。 たとえば端末用の UART デバイスの場合なら , レ す値も , 同じく #define しておきます。 トアドレスのことて、す。コマンドやステータスを表 やコマンドレジスタなどが割り当てられているポー スタのアドレスとはデバイスのステータスレジスタ す。キャラクタ型のデバイスの場合 , デバイスレジ すが , これらは当然ながらデバイスによって違いま バイスドライバ ( のソースの ) 冒頭部分て # define しま デバイスのレジスタは , そのアドレスや内容をデ て buf. h はプロック型のドライバて、は必ず使います。 tty. h はキャラクタ型のドライバて、使います。そし #define RTDATA (UART BASE 十 1 ) / * 送信レジスタ * / #define RSTATS (UART BASE 十 2 ) / * ステータスレジスタ ( リード時 ) * / #define RCNTRL (UART BASE 十 2 ) / * 制御レジスタ ( 同上のライト時 ) * / / * ステータスレジスタの内容の定義 * / #define SRRDY OXOI / * 受信データレディ * / #define STRDY 0X02 / * 送信レディ * / #define SPERR Ox08 / * パリティエラー * / #define SCTOS 0X40 / * クリアツーセンド * / デバイスドライバが実行する機能は , デバイスの 種類によって違います。どのデバイスにも open や close のルーチンがあり , これがデバイスの I / O の実行を許 可します。 open ルーチンは , デバイスの指定が有効 て、あることや , デバイスがレディて、あることをチェ ックし , それからデバイスを初期化します。 open ル ーチンはプロセスがそのデバイスを使用するたびに 実行されます ( 訳注 9 ) 以下に示す open ルーチンは , td と名づけた一般的 VOid tdopen(int device , int flag) な端末デバイス用のものて、す。 if(UNMODEM(device) > = NTDEVS) { / * デバイス番号をチェック * / / * ローカル変数の宣言 ( ここて、は細部を省略 ) * / チェック * / / * それをオーバライドてきるスーパユーザかを 使用中ならば * / / * デバイスが使用中かチェック , return ; seterror(ENX 工 0); 主 1 ] こ存じのように MS ー DO デ バイスドライバは諺朱なヘッ タ郡などを要し , このためア センプリ言語て書く部分力 須となり , 全体を高級言語で 書くことは困難です。 主 8 ] プロセスのデータのうち , ス ワッヒ。ング操作などのために 膨要なのでメモリに常駐しな ければならない部分を p 「 0 頁 域プロセスがメモリ上て生 きているときだけ要な部分 ( = ディスクへスワッヒ。ングさ れてもよし郵分 ) を use 或と 呼びます。デバイスドライバ の一一 ) ルーチンが use 罠或 のデータを参照します。 主 9 ] OP en, CIOS 師 ) ルーチンは必 須でまありません。利用権を 厳密に里する膨のない単 純なデバイス用のドライバで は省第できます。 Device 0 「 i 「 5 for UNIX UN Ⅸのデノヾイスドライノヾ 21
Device 主 16 ] 上記のことはたとえば , かな り大きい文字バッフアを備え ている UART ならば , 1 文字の I/O ごとに割明を信号させ るよりも , バッフアに文字が 満杯になったころをみはから って ( = みはからえるタイミン クで ) 定期的にポーリングした ほうが , CP まはる力に楽がで きる , という意味です。ただ し , 割り込み要求の要因 を多様に胸できるなどの , ある程度のインテリジェント 皀をもった冖 ) UART を吏 うほうが , さらに有利かもし れません。 UNI 肋蕘りなオペレーティ ングシステムであるとさ れ世界中て・急速に普及した 最大の理由が , この記事で述 べられているような I/O インタ フェイスび←化です。里 酌なターゲットカ舸であ 統一的に open は read(l write()ta•どで I/O ができる , という UN Ⅸの l/ 瞬皀を支え る中心なソフトウェアがデ バイスドライバです。この 相手カヨ可で - きょ I/O イ ンタフェイスを使用できると いう巒皀が , UNIX System V R 引 e a s e 3 . 0 以降は STREAMS という機構によっ て , キャラクタ I/O を行うネッ トワークハードウェアおよび 通信プロトコルなどの多生 に対しても適用できるように なりました。このには , 複数の I/O 源を参照し膨要な ら ( に同期で / 0 を実行できる ための , P011 ( ) というシステ ムコールが提供されています。 明主 15 ] 一定時間カ軽っと , そのデバ イスの intr ルーチンを強齣 に実行する , なと・び里をし ます。 24 CMAGAZINE 19 2 0 新しい の使用 デスドアパ きる割り込みの喪失や , デバイスの動作不能が挙げ 正しく書けているとすると ) , 割り込みの保留中に起 デバイスドライバに共通する間題として ( コードは こしてしまうてしよう。 てきますが , 注意しないと暴走やハングアップを起 を使うと変数やアドレスの値を直接に変えることも 中にカーネルのメモリを調べることがてきます。 adb というデバッガを使って , デバイスドライバの実行 デバイスをスーパューザからテストするときは , adb 使って , ドライバの動作をトレースすると楽てす。 り , コンソールなどほかのデバイスへの getchar() を デバイスドライバのデバッグは , printf ( ) を使った こともたいせってす。 は必要な措置てす。また , スタックを最少に抑える し clist などのバッフアのデータの破壊を防ぐために 割り込みの保留は最少に抑えるべきて、すが , しか て浮動小数点演算を行ってはいけません。 に sleep( ) や seterror( ) を使ってはいけません。そし ことがふたつあります。割り込みの保留中 デバイスドライバのプログラミングて , やっては バをつくるときの腕前とされます ( 訳注 14 ) け少なくしてデバッグすることが , デバイスドライ なります。したがって , リプートの回数をて、きるだ ドライバの変更は , この全工程を繰り返すことに トします。 にシステムをリプートしてデバイスドライバをテス ンクし , それを /devi•ィレクトリにおきます。最後 次にドライバ全体をコンパイルしてカーネルとリ ルーチンなど ) のアドレスを登録します ( 訳注 13 ) ルーチン ) と , そのほかのルーチン ( そのデバイスの open るための配列に割り込みハンドラ ( そのデバイスの intr えます。まず , デバイスのエントリポイントを記述す ドライバは一連の手順を踏んて、 UNIX システムに加 これを防ぐために , ほとんどのデバイスド られます。デバイスがハングしてしまう , という問 題てす。 ライバにはタイムアウトルーチンが含まれています ( 訳注 15 ) spl.. ( ) 関数を適切に使うことによって , デバッグ の間にこれらの問題を突き止めることがて、きます。 典型的なケースとしては , 割り込みが起こるはずの 一定時間が過ぎても起こらなかったら , デ ときに バイスを直接にチェックします。割り込みが失われ るときは , コードてそれをシミュレートて、きます。 プロック型のデバイスドライバは一般的に , 割り 込みを使う書き方をします。しかし , デバイスドラ イバがデバイスのステータスを頻繁にチェックしな ければならないようなキャラクタ型のデバイスには , ポーリングを使うプログラマが多くなっています。 割り込みを待つ必要はなくなりますが , CPU のサイ クルは使います。ポーリングは大容量の外部記憶装 置などには向いていませんが , キャラクタ型のデバ イスには有利な場合があります。 デバイスドライバを割り込み型て、書くと , 19200 ポ ーの端末は毎秒約 1920 回の割り込みを起こし , オペ レーティングシステムはこれだけの回数割り込みを 受けてデバイスドライバに再入しなければならない ことになります。割り込みルーチンをポーリングル ーチンに置き換えれば , 一定数の文字を保持するバ ッフアを使うことによって , CPU を要求する頻度が 大幅に減ります。リアルタイムのデバイスもポーリ ングを使用すれば , 割り込みて℃ PU を酷使すること がないのて、 , 有利だといえます ( 訳注 16 ) デバイスドライバは , 書くのは難しくありません が , デバッグは困難になりがちてす。とくに ほか のプロセスやデバイスを妨害しないための注意が必 要て、す。しかしデバイスドライバが正しく動いたと きの満足感は , 普通のプログラムが完成したときと はひと味違って , 格別なものがあります。 Tim Parker 氏は fCOMPUTER LANGUAGE 』誌 の UN Ⅸ担当編集者て、す。
第を上豊望 テパズドライバ LANGUAGE 提携記事 Device 0 「Ⅳ e 門 for UNIX UNIX TI Parker 岩谷宏き テパイスドライバは , 八一ドウェアー存る部分 を吸収し , 周辺機器を制御するソフトである。 LJNIXC は , 周辺機器はテパイスドライバによりフ アイルとして扱われる。システムにとって , このテ デノヾイスドライノヾはオペレーティングシステムと 周辺装置との間のインタフェイスを提供します。デ バイスドライバは普通 , オペレーティングシステム からの I/O リクエストを受け付けて , そのリクエスト の実行をデバイスに命ずる機能をもっています。デ バイスとオペレーティングシステムとの間のインタ フェイスの形式は , デバイスの種類を問わす統一さ れています。 UNIX て、はシステムに接続されるそれぞれのデバイ スに対してデバイスドライバを使用し , デバイスド ライバのコードは UNIX のカーネルと一体化されま す。オペレーティングシステムはデバイスをファイ ルと同等視し , ファイルと同じように open(), read( ) , write( ) , close( ) などのアクセスや , リダ イレクト , パイプなどがて、きます。 デバイスドライバに記述されるデバイスは , ファ イルと同列視されるところから , 普通のファイルと 区別して特殊ファイル ( スペシャルファイル , special file) と呼ばれ , /dev というディレクトリにおかれま す。オペレーティングシステムはそれぞれのデバイ ス , すなわち特殊ファイルに番号をつけ , この「デバ イス番号」によりデバイスを区別します。 UNIX のデバイス番号はふたつの部分からなりま す。メジャー番号 ( major number) は , デバイスの種 バイスドライノヾは必要不可欠である。本記事では , LJN Ⅸでテパイスドライノヾをつくるための諸注意事 項を提示し , キャラクタ型とプロック型の違いや ioctl( ) , splx( ) , c ⅱ st 構造体の意味内容を探る。 類を特定します。そしてマイナー番号 ( minor number) は装置を特定します。たとえば複数のディスク装置 があるとき , デバイスドライバはひとつ , つまりメ ジャー番号はひとって、 , マイナー番号によって装置 を区別します。デバイスを使用するときは , そのデ バイスを表す特殊ファイルが、、オープン〃され , そ のデバイスのエントリポイントのテープルにアクセ スします ( 訳注 1 ) UNIX のデバイスは , キャラクタ型のデバイスか プロック型のデバイスか , またはその両者のどちら かて、す。プロック型のデバイスドライバがもっとも 一般的て、 , これはプロセスが I / O データをプロック単 位て、カーネルのバッフアに読み書きします ( そしてカ ーネルはノヾッフアのデータをプロセスのメモリに読 み書きします ) 。プロック型のデバイスはもともとデ イスク装置用に考えられた機構て、すが , 現在て、は実 質的に , 大容量の外部記憶装置のすべてに対して使 われています。 キャラクタ型とプロック型のデバイスの , おもな 違いはふたつあります。キャラクタ型の I/O はカーネ ルのバッフアを介さずに , 直接デバイスドライバと メモリの間て行われます。 I/O リクエストは通常 , キ ャラクタ型のデバイスドライバには直接渡されます。 端末装置やプリンタは明らかにキャラクタ型のデバ 主 1 ] UN Ⅸオペレーティングシステ ムとデバイスドライバとの主 要インタフェイスは , デバイ スドライバの主要ルーチンの 開始アドレスの酉」であり , メジャーデバイス番号とは , この酉リへのインデクスです。 上記の感工ントリポイントの テープル〃とは , ドライバの 主要ルーチンの開始アドレス の配列 , という意味です。 read(l write( ) なと・の / 0 シ ステムコールルーチンは , れらのアドレスをコールして I/O などの実 f を行います。 なお , UN Ⅸのデバイスドラ イバはカーネルと一体勺にコ ンバイルされているため , MS ー DO ) デバイスドライ / ) よ うに , プート時に読み込まれ る個別の実行ファイルとして ディスクなどの上にする わけではありません。したが ってデバイスが / dev ディレク トリにおかれるというのも , たんに勺 ( 里的 ) な意味 です。 Device 0 「 i e 「 5 fO 「 U N ー X UN Ⅸのデノヾイスドライバ 19
明主 2 ] プロック型デバイスでまプロ セス ( ユーザプログラム ) がカ ーネルのバッフアを相手に I/O をするので , たとえばリード したいプロックがバッフアに あ里的な I/O を要求する ためのデバイスドライバへの コールは行われないという 点カ軒心です語里的な I/O が 行われる場合も , キューイン グを介してリクエストとは非 同期に行われます。デバイス から ( へ ) のデータはカーネル のバッフアに ( から ) 車され ます。 1 プロックとは通常 , 512 バイトまたは 102 ヾイトで す。 靺装置やプリンタなど , 文 字とおりのキャラクタ ( = 文字 ) を I/O するデバイスに対して は , 通常 clist というキ朱なテ・ ータ構告のバッフアをデバイ スドライバカ呆して使用し ます。 主 3 ] strategy ルーチンは一定の 単イ st 「 ategy ) に基、て I/O リクエストのキューイングを 行うことが主務であり , 実際 の read/writei< このルーチ ンの中で行われるとは限りま せんたとえばデバイスがビ ジーなら , read/write'i 後 てり込みハンドラの中で行 われたりします。 主 4 ] デバイスのタ里終了割り込み に対応する割り込みタ里は , デノヾイスドライ / ) intr 儿 チンとして記述しておき , 割 り込みを受けたカーネルはそ のルーチンをコールします。 十分な配慮の盛り込まれた intr ルーチンを書いておく必 要があります。 主 5 ] 割り込みをディスエープルに してから行うべき里とは , 単一 CP ) システムの場合 , リ ンクトリスト ( clis ど ) を操 作するや , チェックした 条件によってはほカルーチ ンから起こされるまで sleep ( ) に入るタなどです。 圧主 6 ] spl.. ( 潤数は , sp10 ( 淞ら S017 ( ) まてが提供されていま す。どれがどのように皀す るかは , システムの構成およ び CPL やバスなどハードウェア 差の生によります。 20 CMAGAZINE 19 2 ′ rivers fo 「 UNIX イスて、すが , カーネルのバッフアを介さないぶん高 速てあり , そのためにプロック指向のデバイスがキ ャラクタ型の I/O を用いることもあります ( 訳注 2 ) 。 プロック型のデバイスは ,strategy ルーチンと呼ば れるルーチンて、 1 プロックのデータのリード / ライト を行います。キャラクタ型のデバイスては , ioctl と いうルーチンを使ってデバイスをさまざまに制御す ることがて、きます。それを使うために , プロック型 のデバイスがキャラクタ型のドライバを設けること が , ときどきあります ( 訳注 3 ) 型が何て、あれドライバは , デバイスに対するリク ェストがあると , つねに一連の基本的な仕事を実行 します。まず , デバイスが使える状態て、あるかどう かチェックし , OK ならばそのデバイスカ”オープンク され , コールした側にアクセスを許します。リード またはライトのコマンドが実行され , それからその デバイスはヾクローズ〃されて , ほかのプロセスか 割り込み らのアクセスが可能になります。 ては困るというキワドイ部分ては , 割り込みをディ こて、割り込みが起き デバイスドライバの中の , ともありえます ( 訳注 4 ) スドライバが , 別の割り込みによって中断されるこ 割り込みからリクエストされた I / O を処理中のデバイ せん。割り込み状態を注意深くチェックしないと , スドライバの動作を中断するものて、あってはなりま 一定の制御された状況以外ては , カーネルやデバイ ないような措置を実行します。またこの割り込みは , をしたり , プロセスに ( 中断以外の ) 悪影響を及ばさ にあり , その割り込みが有効て、あることのチェック り込み処理ルーチンは通常はデバイスドライバの中 用に定義されているルーチンを実行します。その割 そのとき実行中だった仕事を中断し , その割り込み 割り込みを受けるとオペレーティングシステムは , 号されます。 がそのデバイスを使用て、きる状態になったとき , 信 り込みは I / O コールの処理が終了し , ほかのプロセス 起するために , デバイスから送られる信号てす。割 割り込みはオペレーティングシステムの注意を喚 スエープルしておきます。割り込みをディスエープ ルにするには , CPU の現在の割り込み許可レベル を , ディスエープルにしたい割り込みのレベル以上 に設定します ( 訳注 5 ) 割り込みのプライオリティの操作には , SP15(), sp16( ) , SP17(), splx( ) , という 4 つの関数を使い ます。最初の 3 つのうちのどれかをコールすると , 処 理中の割り込みが無視されます。 sp15 ( ) はディスク 装置 , プリンタ , およびキーポードからの割り込み をディスエープルにします。 SP16( ) はさらにシステ ムクロックもディスエープルにします。 SP17( ) はシ リアルデバイスも含めてすべての割り込みをディス ェープルにします。これら 3 つの関数は , コールされ た時点の割り込みレベルの値を返します。そしてそ の値を引数として splx ( ) をコールすると , 元の割り 込みレベルに戻ります。 デバイスドライバの中て、のさまざまなレベル変更 は , 次のように記述します。 int levle a, level b ; sp15( ) ; level a / * 元の割り込み許可レベルを level a にセープ * / / * ディスク装置から割り込まれては困るコードを splx(level b); 書く * / / * どんな割り込みがあっても困るコードをここに level b sp17( ) ; こに書く * / / * 最初の割り込み許可レベルに戻った * / splx(level a); 残り部分をここに書く * / / * ディスク装置から割り込まれては困るコードの たいてい s 15 ( ) て十分なのて、す ( 訳注 6 ) イ部分のコードを割り込みから保護するためには , アル I/O の文字落ちが生じることがあります。キワド 失われる場合があるし , SP17 ( ) は長引かせるとシリ ません。 sp16 ( ) て、はシステムクロックの時計機能が 16 ( ) と sp17 ( ) を使うのは ( 通常は ) 賢明てはあり
ロセスのメモリからデパ、イスドライノヾのパ、ツフアに start ルーチンというものを通常用います。これはデ データをコヒ。ーします。すべてのデータをコビーし バイスのキューからリクエストやデータを取ってデ たか , またはバッフアが満杯になったら , バッファ バイスに送るものて、す ( プロック型のデバイスて、は が空になるまて、デバイスへの物理的な I / O が行われ , strategy ルーチンて、データがキューイングされ , キ それからプロセスはまた繰り返されます ( 訳注 10 ) ャラクタ型のデバイスて、は clist が使われます ) 。デバ イスへの命令がくると , start ルーチンはビジーフラ データはプロセスのメモリから cpass( ) という単純 な関数を使って読まれ , この関数はメモリの終端に グを、、ビジー〃にして保持します 達したら一 1 を返します。逆にデータをプロセスのメ デバイスが処理を終了すると , intr ルーチンが実行 モリに書き込むときは , passc( ) という関数を使いま され , そこて、デバイスは次のプロセスが使えるよう に再初期化されます ( 訳注 12 ) 。キャラクタ型のデバイス す。例示的な端末デバイス用の write ルーチンは , 下 己のようになります。 ドライバて、は , ioctl ルーチンによって特殊な処理を 実行て、きます。たとえば , ドライバとオペレーティ ングシステムとの間の通信方法 ( 例 : ポーレート ) の 変更 , デバイス依存の操作 ( テープの巻き戻し , メモ リのアロケーション等 ) などて、す。 ioctl 関数の例を , 端末デバイスの場合として示し ます。この ioctl ルーチンは , デバイスのパラメータ をセットする別の関数をコールします。コールされ 主 13 ] る関数のコードはここには示しませんが , コメント 通常これは , conf. c というソ 大量のデータ転送は copyio ( ) というプロセスが処 によって仕事の内容はわかるて、しよう。 ースファイルの中の酉リ宣言 を書き換える作業です。 理します。この関数には引数として , ソースとデス 区主 14 ] ティネーションのアドレス , 転送バイト数 , そして VOid tdioctl(int device,int cmd,int mode デノヾイスドライバの 4 里的モ ジュー丿くできず , カーネ 方向フラグ ( リードかライトか ) を与えます ( 訳注 11) faddr targ) ルと一体イヒコンバイルすると read 関数はキャラクタ型デバイスからメモリへの いう UN Ⅸの方法のために“デ バイスドライバのインストー 転送を行います。操作は write の場合と同様て、す。端 if(ttiocom(&td tty [UNMODEM(device)) , ル方法だけは MS ー DO ) ほう カ憂れている气といわれる場 末デバイス用の read は次のようになるて、しよう。 cmd , arg, mode)) 合があります。しかしデバイ スドライバの形式や糸竦事は , UN Ⅸのほうが単純です。 void tdread(int device) また , カーネルとデバイスド ライバとび )- 一体化コンバイル をユーザが行う UN Ⅸの方式 は , 0 ) ソースをユーザが入 手できることを意味していま す。これは , 基本ソフトウェ アの当共方式として MS-DOS よりもはるかにまともであり , ソフトの提供に必すソースも イ寸随させることは , ユーザを 尊重した道勺に健全な姿勢 であるといえます。 Device 0 「 i e 「 5 fO 「 UNIX 一三ロ void tdwrite(int device) register struct tty * tp; tp = &td tty CUNMODEM(device)] ; ( * linesw CtP— >t line] . 1 write)(tp); tdparam(device); register struct tty * tp; tp = &td ttyCUNMODEM(device)] ; ( * linesw [tp— >t line] .1 read)(tp); void tdparam(int device) / * 変数を初期化 * / / * 参照する回線のアドレスとフラグを得る * / addr=td addr [UNMODEM(device)] ・ cflag = td ttyCUNMODEM(device)] . t cflag; / * スヒ。ードをチェックし 0 なら回線をハング * / / * スピード変更のセットアップ * / / * 回線制御のセットアップ * / / * 割り込みの管理 * / read や write< 複数の文字をコピーするときは , 1 文字すっこれらをコールするのて、なく , clist という 小さなバッフアが使われます。このバッフアは一種 のリンクトリストてあり , これに文字を入れたり出 したりするには putc ( ) と getc ( ) を使います。 clist の ヘッダ部に文字数が記録されます。 プロック型とキャラクタ型のデバイスの両方て、 , UN Ⅸのデバイスドライバ 23
三田典玄の 実践 C プログラマ 養成講座 第⑤回日 S ー 232C の制御 行のこの種のマニュアルは部品会社が普通 10 TREM. RESMAKEFILE に使われ はただて、お客に配るものなのて、 , あまり手 るリンカ応答ファイル に入らないかもしれない パソコンそのもののこういった資 以上の . C ファイルと . ASM ファイルを 9 の 料はメーカーによっては社外秘扱いて、一般 MAKEFILE て、アセンプル / コンパイル / リ デバイスドライバ本体 1 S 工 0 . C には出してくれないものも多い。仮に出て ンクするとサンプルの通信ソフトがて、きあ タイマ割り込みサービ 2 SIGALM . C いたとしてもプログラムを組むのに必要な こから先は各自の好みに応じてプ がる。 スノレーチン 肝心な部分が書かれていなかったり , ある ログラムを拡張してみるとよい RS ー 232C の割り込みサ 3 RSINT. ASM べき表などの類が不足していたり , とにか ービスルーチン く不親切このうえない。アメリカの IBM-PC 4 SIGALMS. ASM タイマ割り込みサービ とそのコンパチて、あれば , まずメーカーが スノレーチン そんな意地悪をユーザにすることはないの アセンプラて、の CLI , 5 CLI. ASM て、安心て、きる。 このプログラムはハードウェアをやった STI 命令を C 言語て、呼 私もこのプログラムをつくるにあたって ことがないとたいへん難しく見えるはずて、 べるようにしたもの はメーカーに関係する某所からいろいろな ある。このプログラムを読む ( 書く ) ために RS ー 232C ライプラリの 情報をウラから項くこともあった , 必要な資料の一部は , ソフトウェア関係の 利用のためのヘッダフ ことをここに書いておこう。とにかくそう 資料だけて、はないのて、 , 書店て、は売ってい ァイル いった「政治カ ( ? ) 」もときには技術者には ないものもある。 タイマ割り込みライプ 7 SIGALM . H 必要になることも多いのだ。 たとえばここて、使っているパソコン内部 ラリの利用のためのヘ の IC チップそのものの資料は必須だが , ッダファイル 般の書店て、はそのマニュアルは手に入らな ライプラリ使用方法を いのが普通て、ある。 知るためのサンプルプ チップのマニュアルは , 東京て、あれば秋 ロクヾラム C 言語とアセンプラルーチンを組み合わせ 葉原の部品屋さんにマニュアルをコヒ。ーし 9 MAKEFILE 以上のルーチンをまと る場合は , 必ずその C 言語特有のクセをきち てもらうか , あるいはマニュアルそのもの める MS-MAKE 用の んと認識しなければならない。そのため , を買ってくるとよい。しかし , メーカー発 MAKEFILE プログラムは 1 行 1 行慎重に行う必要のある マあ のはもちろんのこと , アセンプラとのリン ク時にはコンパイルスイッチなどソースリ スト上に現れない問題があまたあることも 知らなければならない 私は今回のアセンプラルーチンを C 言語コ ンパイラに書かせた。この方法はけっこう 有効て、ある。つまり , 今回使った MS - C て、は 「 -Fa 」というスイッチがあり , これを使って まず C 言語のプログラムの簡単なものを書 き , それをコンパイルし , アセンプラソー スリストを得るわけて、ある。そしてそのア センプラルーチンを書き換えて , 必要なア センプラブログラムを書くわけて、ある。 の方法を使うと , 8086 系 CPU などて、はとく プログラムファイルの内容 ハードウェアに関連する プログラムに必要な資料 6 S 工 0 . H メーカーの C 言語フログラム 特有の間題とアセンプラ 8 TERM. C ◆・ ク 手レ 7 け 106 CMAGAZINE 19 2
主 10 ] この / ヾッフアはカーネルが用 意するものではなく , デバイ スドライバを書く際の変 言において礑呆する , struct clist 型 ( 後述 ) という特殊な 構きのバッフアです。 主 11 ] copyio( ま XEN Ⅸシステムの 場合。 U N ー X Sy s t e m V や BSD UNIX では copyin() と copyout ( ゆふたつの関 魏後者のふたつの関数では ソース / デスティネーションの 引数はともに仮想アドレスで す。 主 12 ] star もし一チンはキューからリ クエストを取って厖里的 I/O を 実行する実動ルーチンです。 したがってカーネルから直接 コールされることはありませ ん。デバイスがビジーでなけ れよ startJ し egy ルーチンからもコールされ ます。通常は , I/O±里の終了 →デバイスからの割り込みに よってカーネルに通知→割り 込みルーチンとしてそのデバ イスの intr がコールさそ の中で残務処理としての start をコールする , という流 れになります。 22 CMAGAZINE 19 2 「ⅳ e 門 fo 「 UNIX tp =&td tty [UNMODEM(device)) ・ address = td addressCUNMODEM(device)]; if((tp- >t_lflag & XCLUDE) & & !suser( ) ) seterror(EBBUSY); return ; / * オープンしていなかったなら ttinit( ) をコー ルして初期化を行う * / if((tp- > t_state&(ISOPEN ー WOPEN)) = = 0 ) { ttinit(tp); / * フラグを初期化し tdparam ( ) をコールして回 線を設定する * / tdparam(device); / * モデムを使っているならキャリアをチェック * / / * ダイレクトならキャリア検出フラグをセット * / / * 割り込みプライオリティをセット * / / * キャリア検出信号を待っ * / close ルーチンはプロセスがそのデバイスへの仕事 を終えた後にのみ使います。このルーチンは , その デバイスからの割り込みをディスエープルにし , 終 了コマンドを発行します。そのデバイスへの未了の 参照が残っていても , それはすべてリセットされま す。いって、も使えるというタイプのデバイスには , close ルーチンは不要て、す。どうしても close が必要 なのは , リムーバブルなメディアや , 専用権を設定 しなければならないデバイスて、す。たとえば一部の モデムは , close によって電話回線を、、受話器を置い たク状態に戻さないと , 次のプロセスが使用て、きま せん。 上記の端末デバイスの場合には , close は次のよう になるて、しよう。 void tdcolse(ind device) register struct tty * tp; tp = &td tty CUNMODEM(device)] ; ( * 1ineswCtp ー >t line] .1 close)(tp); if()p ー >t cflag & HUPCL) tdmodem(device, TURNOFF); / * 専用権ビットをクリアする * / ip—>t flag & = XCLUDE; strategy 関数 (プロック型デバイスのみ ) には , 引 数としてカーネルのバッフアのヘッダ ( のアドレス ) が渡されます。バッフアのヘッダにはリードやライ トの命令 , そして転送先のメモリ番地があります。 バッフアのサイズは通常はシステムによって固定て、 あり , 512 または 1024 バイトて、す ( サイズは usr/include/ sys/param. h の中の BSIZE の定義をみればわかりま す ) 。デバイスのプロックサイズは , バッフアのサイ ズより小さいこともあり , その場合はドライバは複 数回のリードやライトを実行します。 strategy 関数を , ハードディスク用のデバイスド ライバの場合て、例示します。実際のコードはありま せんが , コメント文によってこのデバイスドライバ の機能がわかると思います。 void hdstrategy(register struct buf * bp) / * ドライプとパーティション初期化 * / / * ローカル変数を設定 * / / * ドライプとパーティションの有効性をチェッ ク * / / * 目的とするシリンダを計算 * / / * 割り込みをディスエープル化 * / / * リクエストをキューに入れる * / / * コントローラをチェックし * / / * アクテイプて、なければ起動する * / / * 割り込みレベルをリセットする * / キャラクタ型のテンヾイスは strategy て、はなく write と read の関数を使用し , リクエストされたデバイス の有効性をチェックして , それから write の場合はプ