デバイスファイルと /dev
Linuxでは「すべてはファイル」という思想のもと、ハードウェアもファイルとして扱います。その入口が /dev ディレクトリにある「デバイスファイル」で、ここを読み書きするとドライバを通じて機器とやり取りできます。たとえば /dev/null は捨て場、/dev/sda はディスク全体、/dev/ttyS0 はシリアルポートを表します。アプリは通常のファイル操作(open・read・write)と同じ作法でデバイスを操作でき、その先の橋渡しをドライバが担います。
Linuxには「すべてはファイルである」という一貫した思想があります。テキストや画像のような普通のデータだけでなく、ハードウェアまでもファイルとして扱う、という考え方です。その入口になるのが、/dev というディレクトリに置かれたデバイスファイルです。デバイスファイルは見た目こそ普通のファイルに似ていますが、中に文字や数値のデータが保存されているわけではありません。ここを読み書きすると、その先に控えているデバイスドライバを通じて、実際の機器とやり取りができる——いわばハードウェアへの「窓口」になっています。アプリ側は特別な作法を新しく覚えなくても、通常のファイルと同じ open(開く)・read(読む)・write(書く)といった操作でハードウェアを扱える、というのがこの仕組みの大きな利点です。
/dev に並ぶもの
/dev は、こうしたデバイスファイルが集まる特別なディレクトリです。中をのぞくと、用途のはっきりした代表的なファイルがいくつも見つかります。たとえば /dev/null は、書き込んだものをすべて吸い込んで消してしまう「捨て場」で、不要な出力をここへ流し込んで黙らせるのに使います。/dev/sda は1台目のディスク全体を表し、/dev/ttyS0 はシリアルポートを表します。同じディスクでも、その中の特定の区画(パーティション)には /dev/sda1 のように番号の付いた別のファイルが対応する、といった具合に、機器とその一部分が細かくファイルとして並んでいます。これらはどれも、ファイルという同じ見た目をしていながら、その背後ではまったく違うハードウェアにつながっています。「ファイルを操作する同じ作法で、捨て場もディスクもシリアル通信も扱える」というのが、/dev というディレクトリが体現している世界観です。
なぜファイルとして扱うのか
ハードウェアをファイルに見立てる最大の利点は、操作の作法を統一できることにあります。もし機器ごとにまったく別の特殊な命令を覚えなければならないとしたら、新しい機器を扱うたびに学び直しが必要になり、プログラムも機器ごとにばらばらの作りになってしまいます。ところがファイルという共通の枠に当てはめておけば、開いて・読んで・書いて・閉じる、という同じ手順でさまざまな機器を扱えます。たとえばシリアルポートにデータを送りたいなら、対応するデバイスファイルに書き込むだけで済みますし、その同じ書き込みという操作が、別の機器では別の意味の制御として働きます。アプリ開発者の側から見れば、相手がディスクなのか通信ポートなのかをいちいち意識せずに済み、その違いはすべてドライバが引き受けてくれます。さらに、ファイル操作にはパーミッション(アクセス権)という既存の仕組みがそのまま使えるので、どのユーザがどの機器を読み書きできるかも、ファイルと同じ感覚で管理できます。この「共通の窓口」があるおかげで、Linuxは多種多様なハードウェアを見通しよく、統一的に扱えるのです。
アプリ・デバイスファイル・ドライバの関係
実際の流れを追ってみましょう。アプリは、まず目的の機器に対応したデバイスファイルを open システムコールで開きます。続いて read でデータを受け取ったり、write でデータを送ったりし、用が済んだら閉じます。このとき、ファイルへの読み書きの要求は、そのままドライバの中に用意された対応する処理へと橋渡しされ、ドライバが実際のハードウェアを動かして、その結果をアプリへ返します。ドライバ側は、open や read、write といった操作のそれぞれに応える処理をあらかじめ備えており、アプリからの要求が来るたびにカーネルがそれらを呼び出す、という形で連携します。つまりデバイスファイルは、ユーザ空間で動くアプリと、カーネル空間で動くドライバとをつなぐ接点として働いているわけです。アプリは「ファイルを操作しているつもり」でいて、その裏側ではドライバが特権を使って機器を制御している、という二段構えの構造になっています。この境界をまたぐデータの受け渡しには専用の作法があり、ドライバを自分で書くときに重要になってきます。
中身を確認する
/dev の様子は、ふだんのファイル操作と同じコマンドで確かめられます。ls /dev で一覧を見れば、null や sda、ttyS0 といったファイルがずらりと並んでいるのが分かります。さらに ls -l を使うと、行頭の1文字でそのファイルがデバイスファイルかどうかが分かり、後の回で詳しく扱う「担当ドライバを示す番号」まで読み取れます。デバイスファイルが正しく用意されているか、目的の機器に対応するものが存在するかを調べるときの、最初の一手になります。何か機器がうまく扱えないとき、まず /dev に対応するファイルがあるかを見る、という習慣をつけておくとよいでしょう。
ファイルは静的ではない
かつて /dev のファイルは、考えうる機器のぶんだけ、あらかじめ手作業で大量に並べておくものでした。しかし現代のLinuxでは、udev というしくみが機器の接続や切断をその都度検知して、必要なデバイスファイルを動的に作ったり消したりします。USBメモリを挿した瞬間に、それに対応するファイルが /dev に現れ、抜けば消える、という振る舞いはこの udev のおかげです。自作のドライバでも、ドライバ側で適切な手続きを踏めば、人が手作業でファイルを作らなくても、組み込んだ時点で /dev にファイルが自動的に現れるようにできます。そしてもうひとつ、自作のドライバ開発でこうした出来事を追うときに頼りになるのが dmesg です。機器を挿した瞬間や、ドライバを組み込んだ瞬間に、認識のログやエラーが時系列で流れるので、デバイスファイルが期待どおりに用意されたか、機器が正しく認識されたかを確かめる助けになります。「/dev のファイルは固定された一覧ではなく、機器の状態に応じて生き物のように変化する」と捉えておくと、現代のLinuxの挙動に戸惑わずに済みます。