キャラクタ型デバイスの概要
デバイスファイルは大きく「キャラクタ型」と「ブロック型」に分かれます。キャラクタ型は1バイトずつ順番にやり取りする機器で、キーボードやシリアルポート、端末などが該当します。ブロック型は決まった大きさのかたまり単位でランダムにアクセスする機器で、ディスクやSSDが代表です。ls -l で先頭が c なら character、b なら block と見分けられます。自作ドライバの最初の題材は、構造が素直なキャラクタ型デバイスが定番です。
/dev に並ぶデバイスファイルは、すべてが同じ性質を持つわけではありません。扱うデータの「やり取りの仕方」によって、大きくキャラクタ型とブロック型の2種類に分かれます。この区別は、既存のドライバを読むときにも、自分でドライバを書くときにも土台になる、とても基本的な考え方です。どちらの型かによって、ドライバ内部の構造も、得意とする使い方もはっきり変わってきます。まずは「データを1バイトずつ流れるように扱うのか、それとも決まった大きさのかたまりでまとめて扱うのか」という違いを軸にして、両者を見比べていきましょう。この軸さえ押さえれば、あとの細かい話も整理して理解できます。
キャラクタ型デバイス
キャラクタデバイスは、データを1バイト(1文字)ずつ、順番にやり取りする機器です。水道の蛇口から水が連続して流れ出るように、データがストリーム(流れ)として行き来するイメージで、途中の好きな位置へいきなり飛んで読む、といった使い方は基本的にしません。先頭から順に流れてくるものを、流れてくる順に受け取る、という素直なモデルです。該当するのは、キーボード、シリアルポート、端末(ターミナル)などです。たとえばキーボードは、押された文字が押された順に次々と届きますし、シリアルポートでも1バイトずつデータが流れていきます。組込み機器のデバイス制御は、このキャラクタ型として作ることが多くあります。LEDを点けたり消したり、スイッチの状態を読んだりといった、少量のデータを順に出し入れする制御と、ストリーム的なキャラクタ型の性格がよく合うからです。やり取りするデータ量が小さく、決まった大きさのブロックにまとめる必要もないため、扱いがそのぶん単純になります。
ブロック型デバイス
もう一方のブロックデバイスは、決まった大きさのかたまり(ブロック)を単位として、ランダムにアクセスする機器です。代表例は、ハードディスクやSSDといった記憶装置です。本棚から任意のページを直接開くように、必要な位置のブロックへ飛んでまとめて読み書きできるのが特徴で、1バイトずつ順に流すキャラクタ型とは発想がちょうど逆になります。ディスクのように大量のデータを保存する機器では、1バイトずつちまちま読み書きしていては効率が悪く、ある程度のかたまりでまとめて扱うほうが理にかなっているのです。そのためブロック型は、読み書きを効率よくこなすために、いったんデータをためておくキャッシュや、アクセスの順番をうまく並べ替えるスケジューラといった仕組みが間に入ります。そのぶん内部の構造はキャラクタ型よりずっと複雑になります。私たちが普段、ディスクにファイルを保存して取り出せるのも、こうしたブロックデバイスの上に、ファイルシステムという仕組みが築かれているおかげです。
ls -l で見分ける
ある機器がどちらの型なのかは、ls -l を使えば簡単に見分けられます。普通のファイルなら行頭が - で、ディレクトリなら d で始まることはすでに学んだとおりですが、デバイスファイルの場合は、先頭の1文字が c か b になります。c なら character、つまりキャラクタ型を、b なら block、つまりブロック型を意味します。たとえば ls -l /dev を実行してシリアルポートの行を見れば先頭が c に、ディスクの行を見れば先頭が b になっているのが確認できます。さらに、この行をよく見ると、普通のファイルでサイズが表示される位置に、代わりに2つの番号がカンマで区切って並んでいます。これは担当するドライバと機器を示すデバイス番号で、型を表す先頭の文字とあわせて読むと、その機器の素性がより詳しく分かります。自作のドライバでデバイスファイルを作るときも、この c か b かを指定して、どちらの型として登録するかをはっきり決めることになります。行頭の1文字を見る癖をつけておくと、その機器の性格がひと目で分かります。
もう一種類あること
厳密に言えば、Linuxのデバイスにはネットワーク型という第3の種類もあります。ネットワークインターフェースカードのように、データを非同期に、しかも大量にやり取りする機器がこれにあたります。ただしネットワーク型は、これまで見てきたようなデバイスファイルとして /dev に姿を現すわけではなく、扱い方もかなり独特です。そのため、ドライバ学習の入口の段階では、まずキャラクタ型とブロック型という2つの分類をしっかり押さえておけば十分で、ネットワーク型は「そういうものもある」と頭の片隅に置いておく程度で構いません。
最初の題材はキャラクタ型
自作ドライバの最初の題材として定番なのは、構造が素直なキャラクタ型デバイスです。理由は明快で、1バイトずつ読み書きするという単純なモデルのため、アプリからの read や write が、ドライバの中のどの処理に対応するのか、という対応関係がそのまま見えやすいからです。ブロック型のように、キャッシュやスケジューラといった込み入った仕組みを意識する必要もありません。実際の学習でも、GPIOにつないだLEDやスイッチを、文字 1 で点灯・0 で消灯し、状態を1バイト読み取る、といった形でキャラクタ型ドライバとして作るのが王道です。
具体的にイメージすると、アプリがデバイスファイルに 1 という1文字を書き込めば、それがドライバの write の処理に渡ってLEDが点灯し、0 を書き込めば消灯する、という流れになります。逆にスイッチの状態を知りたければ、デバイスファイルを read することで、押されているかどうかを表す1バイトが返ってきます。このように、ファイルへの1バイトの読み書きが、そのまま機器の制御や状態取得に対応する——という分かりやすさが、キャラクタ型を入門に向いたものにしています。「ストリーム的に1バイトずつ」というキャラクタデバイスの性格を、手を動かしながら体で覚えることが、ドライバ開発という世界へ踏み込むための最短の入口になります。