デバイスドライバとは、OSの起動時にメモリ上に読み込まれて常駐し、 周辺装置からのハード的な割り込みによって駆動される、OSに付属する 一種のサブルーチン群です。IBMのPCのマニュアルでは 「装置駆動ルーチン」などと呼ばれています(した?)。
デバイスには、以下に示すように、主に2つのタイプがあります。
ある決められた単位(ブロック)でデータの入出力を行うデバイスです。 「ファイルシステムを構成できるデバイス」と考えてください。 つまり mount(8) できるもの、ですね。
1バイトずつデータの入出力を行うデバイスです。今回の「SCSI ターゲット・ドライバ」はこのモデルで作りました。このドライバは、 アプリケーションがSCSIのコマンドを直接解析して動作するという、 「全く汎用性のない代物」です。
デバイスドライバは、以下に示す3つの部分から構成されます。
OSの起動時に、上位(OS)から1度だけコールされる部分です。 ハードウェアの存在チェックを行った後、自分自身をデバイスドライバ としてOSに認識させます。
当該ドライバの場合、この初期化関数だけをグローバル関数とし、 カーネルの他の関数(/usr/src/linux/drivers/char/mem.c)から 呼ぶように mem.c を修正しました。それ以外の内部関数/変数は すべて static としました。この理由は、カーネルは膨大な オブジェクト・モジュールをリンクして作られるため、関数を グローバルにしてしまうと名前の衝突が起こり、リンクで失敗する ことが考えられるので、これを防ぐためです。
上位から(間接的に)コールされる部分です。アプリケーションが open(2) や read(2) のシステムコールを発行すると、OSの デバイスドライバ・カーネル・インターフェース(DKI)機能を 通じてOSから当該関数がコールされます。OSからの周辺装置との 入出力要求を実行し、その結果をOSに返します。図示すると、
ハードウェア割り込みによって駆動される部分です。
上位アプリケーション(以下、APといいます)は、システムコールという OSとのインターフェースを使ってファイル単位の入出力要求を行います。 上位APからはデバイスドライバの存在は見えません。唯一、 /dev/xxx というデバイスを仮想化したファイルが見えるだけです。上位APは、 open(2) でこのデバイス・スペシャル・ファイルをオープンします。
また、ドライバの種類によっては、デバイスドライバ・インターフェース (DDI)という受け口を設けて、上位APからの直接的な指令を受けられる ものもあります。このような指令は、通常のREAD/WRITE以外の、 ドライバ内部の状態通知・設定変更を指示するものが多いです。私が作った ドライバも、ioctl(2) というDDIを利用してAPとのやり取りを行いました。
デバイスドライバ内部では、OSが提供する read(2) などのシステムコール や printf(3) などのライブラリ群(実体は libc.* にある *らしい* です) がいっさい使えません。また、上位APがまだ起動していない(=存在しない) 状態もありうるなど、一般的なサブルーチン・ライブラリとはかなり異なった 環境を持ちます。
UNIXシステムはマルチタスク環境であり、自分が作成したAP以外に 多くのAPまたはデーモン(システムサービスプログラム)が動作しています。 イベント待ちを行う場合、DOSアプリケーションのようにビジーウェイト (イベントが発生するまでその場でループすること)を行うと、他のプロセス に制御が移りにくくなり、システム全体のパフォーマンスが落ちます。 このため、イベントがなかったら、スリープなどにより明示的に他のプロセス に制御を譲ってやる必要があります。このほかに、他のプロセスに制御が移る (自プロセスがスリープ状態になる)機会は次の場合があります。