プログラマーの理想と現実

第1回 デバイスドライバは難しいか?

2014.08.06

この記事は、『UNIX Magazine』2003年8月号(2003年7月18日発売)に掲載された同名記事の初稿(著者から編集部に提出したもの)を元に、Web掲載用に一部を修正したものです。10年以上前に執筆したものなので、現在のUNIXを取り巻く環境とは色々と異なることがありますが、プログラミングに対する心構えとしては現在でも通用するものと思い、再掲してみることにしました。

・・・・・・・・・・

ここ数年、筆者はおもにUNIX系OS用のデバイスドライバを書くことを生業としている。取引先から仕様書をもらってコードを書き、動くものを納めるという職業プログラマである。この商売が成り立つのは、取引先の技術者に、デバイスドライバの開発は難しいから専門家に任せよう、という気持があるからに他ならない。

色々と周りを観察するに、世間一般の計算機技術者がデバイスドライバは難しいという意識を持っているのは、どうも本当のことらしい。食い扶持があるのはありがたいことだが、しかし、本当にデバイスドライバは難しいのであろうか? そのあたりをちょっと考えてみたい。

デバイスドライバは簡単

デバイスドライバは本質的に簡単である。デバイスドライバを初めて開発する技術者には、必ずそう伝えるようにしている。そして、開発終了後には、ほぼ間違いなく「なるほど簡単だ」との感想が返ってくる。まずは、デバイスドライバが簡単であるという根拠を挙げてみよう。

コードサイズ

典型的なUNIX系OSにおける、デバイスドライバのソースコードは、C言語で概ね2000~3000行程度である。2000行と言えば、C言語に慣れている技術者であれば1~2週間で生産できる分量でしかない。実に小さい。しかも、デバイスドライバでは、たった2000~3000行の生産に3ヶ月程度の期間を取ることも希ではない。プログラム規模が小さく、しかも期間は通常の感覚の5~6倍ほども長いのであるから、これを「簡単」と言わずしてなんとしよう。

計算

プログラムとは、計算機上で動作することによって、何らかの意味のある計算を行うものである。デバイスドライバもプログラムの一種であるから、もちろん計算を行う。だが、デバイスドライバの主たる計算とは、単なる「データのコピー」であることが多い。

極論すれば、やっていることはcpコマンドとたいして変わらない。デバイスからカーネル内バッファへ、カーネル内バッファからユーザプログラムのバッファへ、あるいはその逆の経路で、データを単にコピーすることが、デバイスドライバの最大の任務である。複雑な数学的計算やデータ構造の解析などはほとんど必要がない。単にデータをコピーするプログラムが「簡単」でなくて、一体何を簡単と言うのであろうか。

情報

プログラムを開発する上で、解こうとする問題や対象とするシステムに関する情報の有無は、開発作業の困難さに大きく影響する。ごく一般的なプログラム開発では、発注元からの仕様が曖昧だったり、利用するライブラリやシステムの動作が十分に説明されていなかったり、不十分な情報のもとで開発を進めざるを得ないことは多い。

翻って、デバイスドライバの開発では、情報は有り余るほど提供されるのが普通である。通常、デバイスドライバの外部仕様は極めて簡素である。対象とするハードウェアの仕様は、必要であれば回路図からタイミングチャートまで参照できる。ホスト計算機側では、メモリページの割り付け操作やバスの細かい制御、プロセスのスケジューリング、複数プロセサにおける排他制御まで、必要な情報はほぼ確実に参照できる。これだけ完璧に情報が提供され、自由に参照できるのだから「簡単」でないわけがない。

デバイスドライバは簡単?

サイズは小さいし、期間は長いし、やることは基本的にデータコピーだけだし、情報はすべて提供されているのであるから、デバイスドライバは本質的に「簡単」なのである。でも、本当なのか?もし本当に簡単なのであれば、どうして世間一般の計算機技術者は難しいと思い込んでいるのか?

それは、きっとこういうことなのだ。デバイスドライバという一本のプログラムを、完成したものとして見ると、これはもう前述のとおり、全くもって簡単である。開発終了後に担当した技術者から返ってくる「なるほど簡単だ」という言葉は、「なるほど自分の作り上げたものは簡単なプログラムだ」という意味だと思って良い。しかし、デバイスドライバが完成するまでの開発プロセスは、多くの計算機技術者にとって、そう簡単なものではないようなのである。以下、デバイスドライバの開発プロセスが簡単ではなかもしれないという根拠を挙げてみよう。

プログラミング

プログラムを開発する際には、まず何か書いてみる、というところからはじめる技術者が多いように思える。ちょっと作ってはテストし、ダメなら直してまたテストし、動いたならば次をちょっと作ってはテストし…… という感じである。筆者はこの開発手法を 行き当たりばったりプログラミング と呼んでいる。

行き当たりばったりプログラミングの手順

ちょっと考える ⇒ ちょっと書く ⇒ ちょっと動かす⇒ ちょっとテストする ⇒ 続きをちょっと考える ⇒ ちょっと書く ⇒ ……

多くのプログラム開発案件においては、この行き当たりばったりプログラミングは、それなりにうまく行くし、現実に色々な場面で役に立つこともわかってはいる。しかし、デバイスドライバの開発においては、残念ながら行き当たりばったりプログラミングではうまく行かない。

例えば、割り込みハンドラだけを先にちょっと作ってテストして…… と考えたとしよう。さて、割り込みハンドラの動作を確認するには、ドライバモジュールの自動構成ルーチン、デバイスの初期化ルーチン、open(), close(), read(), write()などのドライバエントリルーチンなどなど、全て一通り揃っていて、しかもそれぞれが正しく動作していなければ意味がない。これでは、割り込みハンドラだけ先に…… と考えたのに、本末転倒である。結局、デバイスドライバの構成要素のうち、どれを抜き出してみても、まずは全体として一通り動くものがないと動作確認ができないのである。

つまり、デバイスドライバの開発においては、必要とされる機能や関数を全部一度に作り、全部まとめて動作確認するという 一発完動プログラミング が必須なのである。

一発完動プログラミングの手順

全部考える ⇒ 全部書く ⇒ 全部動作させる ⇒ 全部テストする

どうも、多くの計算機技術者にとって、一発完動プログラミングに馴染みがない、ということが問題のようである。

デバッグ

C言語で良くあるプログラムのミスの一つは、ポインタ変数に0などの不正なアドレスが代入されており、それに気づかずにポインタ変数参照で不正なアドレスにアクセスし、セグメント違反を起こす、というものである。一般のUNIX上のプログラム開発においては、この場合プロセスにSIGSEGVが送られ、実行時メモリダンプであるcoreファイルを生成してプロセスが終了する。プログラマは、引き続きシェルよりデバッガを起動し、coreファイルを参照して(比較的)容易にSIGSEGVの原因を突き止めることができる。

デバイスドライバで同様のミスを犯していた場合、カーネルはたいていpanic して落ちてしまう。引き続くデバッグ作業はできず、不正アドレスへのアクセスを見つけることは容易ではない。状況が悪いと、panicすらせずにカーネル内の重要なメモリを壊してしまい、後々動作がおかしくなる(ディスクへの読み書きがおかしくなるとか、プロセスが動かせなくなるとか、ネットワークへのアクセスができなくなるとか……)という現象が起きる。

このように、デバイスドライバのデバッグは、通常のUNIX環境下でのプロセスのデバッグに比べて、圧倒的に困難を伴う。言うまでもないことだが、デバイスドライバの開発においては、ソースコードレベルのデバッガはほぼ100%利用不可能であり、オブジェクトコードレベルのデバッガでも利用できれば御の字というのが実情である。

しかし、良く考えてみて欲しい。何故デバッグという作業が必要なのであろうか。極端な話、最初から誤りのないプログラムが書ければ、困難を伴うデバッグなど必要ないのである。それは現実的ではないとしても、最初から誤りのないプログラムを書くことを心掛けていれば、少なくとも極端に困難なデバッグを必要とするような誤りは発生しないと期待できる。賢明なる読者諸兄にはおわかりのことと思うが、これはすなわち前項で示した「一発完動プログラミング」をちゃんとやれば良いというだけのことである。

それでも、様々な要因により、動作不良は起きるだろうし、その原因を突き止めなければならない可能性は残る。そうなると、やはりデバッグが必要である。デバイスドライバにおけるデバッグテクニックについては、色々深い考察もあって長くなるので、また別の機会に記すことにしよう。

資料読み

デバイスドライバが難しいと思われている最も大きな要因は、資料読みの段階にあるかもしれない、と思うことがある。デバイスドライバの開発において、絶対に読んでおく必要がある資料とは、「対象デバイスの仕様書」と「デバイスドライバ開発マニュアル」である。残念?なことに、これらは多くの場合英語で書かれている。日本語版が存在することは希である。

一例を示そう。少々古いが、筆者の手元にあるInetl 21143 PCIイーサネットコントローラの仕様書は、次の3冊である。

  • Hardware Reference Manual — 218ページ
  • Datasheet — 52ページ
  • Design Guide — 24ページ

これも筆者の手元にあるAlpha Tru64 UNIXのデバイスドライバ開発マニュアルは、次の3冊である。

  • Writing Device Drivers — 279 ページ
  • Writing Kernel Modules — 190 ページ
  • Writing PCI Bus Device Drivers — 54 ページ

単純にページ数を合計すると、808ページという膨大な分量になる。もちろん、どれも英語で書かれている。少なくとも、これくらいの分量の資料を読んで理解しないと、デバイスドライバを作ることはできない。

あるいは、FreeBSD, NetBSD, Linuxなどで動くデバイスドライバを開発するとしよう。これらのOSには、当たり前だがデバイスドライバ開発マニュアルなどは存在しない。代わりに、OSのソースコードはすべて参照できる。ソースコードがマニュアル代わりの資料ということになる。この場合、大量の英語ではなく、大量のC言語を読まねばならない。

どうも、多くの日本人計算機技術者は、英語の資料を読むことが好きではないらしいのだが、こればかりは克服するしかない。残念ながら、デバイスドライバの開発に、英語読解能力は必須である。

さて、英語であるということは置いておくとして、プログラムを開発するに際して、大量の資料を読まなければならないとしよう。果たして、最初に全ての資料をきちんと読むだろうか?どうもそうでは無いような気がする。ざっと目を通したり、重要だと思われるところだけを読んだりして、とりあえず得られた情報を基に、設計を進めてみたり、プログラムを書いたりすることが多いように思える。少し作って、何か問題に当たったり、疑問に思うところがあった時に、また資料を読む。作っては読み、読んでは作り、動かしてみては読み……

賢明な読者諸兄は既におわかりだと思うが、これは「行き当たりばったりプログラミング」と本質的に同じ思考である。行き当たりばったりプログラミングでは、デバイスドライバは作れないことは上に示した通りであるから、「行き当たりばったり資料読み」でもデバイスドライバは作れない。まず、腰を落ち着けて全ての資料をきちんと読み、デバイスの仕様やカーネル内部の機構などを完全に把握してから、プログラムを作るというように、「一発完動資料読み」を実践しなければならないのだ。

計算機の成り立ち

デバイスドライバは、デバイスすなわちハードウェアを相手にするプログラムである。デバイスの仕様は、上述のように仕様書に詳細に記されているのが普通であるため、確実に把握することが可能である。ただし、どのような書物や資料でもそうであるように、ある程度の前提となる知識を要求される。これは、デバイスドライバ開発マニュアルにおいても同様である。

ここで前提として必要とされる知識は、プロセッサ、バス、メモリ、仮想記憶、論理回路(ブール代数)などなど、計算機の基本的な構成要素の仕組みや成り立ちについてである。これらは、計算機屋として身を立てようと志したのであれば、基礎中の基礎として把握しておくべき事項であり、そう難しいものではないはずなのだが、意外とこういった基礎的事項を身につけずにやっている計算機技術者が多いのも残念ながら事実のようである。

計算機の仕組みがわからないから、デバイスドライバは難しい、というのは、まったくもって当たり前である。しかし、これらの基礎的事項を身につけていないのであれば、普通のプログラムの開発でも難しいのではないだろうか。

ドライバ書きになるには

結論は明白である。デバイスドライバの開発は、別に難しくもなんとも無いのである。難しいと感じるとしたら、それは、

  1. 一発完動プログラミング/資料読みに慣れていない
  2. デバッグ技術が不足している
  3. 英語が読めない
  4. 計算機の基本的構造を把握していない

のいずれかに引っ掛かっているのである。3. の英語読解と4. の計算機の基本的構造については、勉強して克服するしかないが、1. の一発完動プログラミングと2. のデバッグ技術については、ちょっとした発想の転換が必要になると思われる。次回は、これらについて記してみたいと思う。

注釈

  • 初稿に由来する表現の差異、ならびにWeb掲載に伴う修正のため、雑誌掲載の内容とは一部文章等が異なることがあります。
  • 転載許諾を頂いた株式会社KADOKAWAアスキー・メディアワークスブランドカンパニーならびに旧『UNIX Magazine』編集部の皆様に深く御礼を申し上げます。

著者プロフィール

tom

当社設立直後に入社して約30 年、UNIX の移植、日本語化、デバイスドライバ開発、周辺機器ファームウェア開発などに継続的に携わり、現在も現役でUNIX 系OS の移植、改造などの開発業務を行う。社内でもっともプログラムを書いている人の一人。代表取締役社長。

記事一覧Index