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