🐧 Linux 総合学習プラットフォーム
デバイスドライバ実装 ・ 上級

カーネルモジュールとは

Linuxカーネルは、機能をあとから追加・取り外しできる「カーネルモジュール」という仕組みを持っています。デバイスドライバの多くはこのモジュール(拡張子 .ko)として作られ、必要なときだけカーネルに組み込めます。カーネルを丸ごと作り直さなくても機能を足せるため、ドライバ開発と動作確認の単位として使われます。モジュールはユーザ空間のプログラムとは違い、カーネル空間で動く点が最大の特徴です。

Linuxというシステムの中心には、ハードウェアを直接握り、メモリの配分やプロセスの実行を取り仕切る「カーネル」と呼ばれる中核プログラムがあります。私たちが普段使うコマンドやアプリは、このカーネルにお願いをして仕事をしてもらう立場にすぎません。そのカーネルには、機能をあとから付け足したり外したりできる拡張のしくみが備わっていて、その部品ひとつひとつをカーネルモジュールと呼びます。デバイスを動かすためのソフトウェアであるデバイスドライバの多くは、このカーネルモジュールの形で作られます。ファイルとしては拡張子 .ko(kernel object の略)を持ち、必要になったときだけカーネルに組み込み、不要になれば取り外せます。普段あなたが書くアプリが拡張子のない実行ファイルになるのとは別物だ、という点を最初に押さえておきましょう。

なぜモジュールという形なのか

カーネルモードで動くプログラムの作り方には、大きく分けて2つあります。ひとつは、ソースコードをカーネル本体と一体にしてコンパイル・ビルドする方法です。これは確実に動きますが、少し直すたびにカーネル全体を作り直し、そのうえで再起動しなければ反映されません。カーネルのビルドは規模が大きく時間もかかるため、開発のたびにこれを繰り返すのは大きな負担です。もうひとつが、必要なときだけ部品のように差し込めるカーネルモジュールとして作る方法です。モジュールはカーネルとは別に用意しておき、稼働中のカーネルに後から組み込めます。カーネルを丸ごと作り直さずに機能を足したり引いたりできるため、ドライバの開発と動作確認をこの小さな単位で回せます。書いて、組み込んで、ログを見て、外してまた直す——この短いサイクルを支えるのがモジュールという仕組みであり、だからこそドライバ開発の基本単位として使われます。

ユーザ空間とカーネル空間

モジュールを理解するうえで欠かせないのが、プログラムの動く「場所」の違いです。Linuxで動くプログラムは、アプリやコマンドなどが暮らすユーザ空間と、カーネル本体やドライバが動くカーネル空間に分かれています。CPU自身にユーザモードと特権モード(カーネルモードとも呼びます)という動作の段階が用意されていて、この2つの空間はそれぞれのモードに対応します。ユーザ空間のプログラムはユーザモードで動き、自分に割り当てられたメモリの外には手を出せず、ハードウェアを直接操作することも許されていません。これは、暴走したアプリが他のプログラムのデータを壊したり、勝手にハードウェアをいじったりするのを防ぐための、システムを守るしくみです。一台のコンピュータを複数の人やプログラムが安全に共有できるのは、この分離があるからこそです。

一方でカーネル空間は特権モードで動き、ハードウェアへ直接アクセスできる強い権限を持ちます。カーネルモジュールはこのカーネル空間で実行されるため、ユーザ空間のアプリには禁じられているハードウェア制御が可能になります。ドライバがモジュールとして作られるのは、まさにこの特権が必要だからです。ただし力が強いぶん責任も重く、モジュールの不具合は、アプリのように自分だけが落ちて済む話では終わりません。カーネルは全体でひとつのまとまりとして動いているため、ひとつのモジュールの誤りがカーネル全体を巻き込み、システムごと停止させてしまうこともあります。「強い権限の場所で動いている」という緊張感が、ドライバ開発では常について回ります。

アプリとの決定的な違い

普通のCプログラムは main 関数から始まり、プログラム自身が処理の流れを動かしていきます。ところがカーネルモジュールには main 関数がありません。代わりに、組み込まれた瞬間に一度だけ走る初期化関数と、取り外されるときに走る終了関数を用意し、それらを「これが入口、これが出口」とカーネルに登録しておきます。あとは適切なタイミングでカーネルの方から呼び出してもらう、という受け身の作りになります。自分から動き出す主役ではなく、舞台監督であるカーネルが「今だ」と合図したときだけ出番が来る役者のようなものだ、と捉えると感覚がつかめます。この発想の転換が、アプリ開発からドライバ開発へ移るときの最初の関門です。

実務での位置づけ

実際のLinuxには、すでに膨大な数のモジュールが用意されています。ネットワークカードを動かすドライバ、USB機器を扱うドライバといったものがモジュールとして提供されていて、機器をつないだときに必要なものが自動的に読み込まれます。いま自分のシステムにどんなモジュールが組み込まれているかは lsmod というコマンドで一覧でき、個々のモジュールが誰によって作られ何をするものなのかは modinfo というコマンドで調べられます。これらのモジュールは、カーネルのバージョンごとに決められた場所(/lib/modules の下に、uname -r で得られるバージョン名のディレクトリがあり、その中)にまとめて置かれていて、システムが状況に応じて適切なものを選び出します。Linuxが世界中のさまざまなハードウェアに対応していて、インストール直後から多くの機器がそのまま使えるのは、こうしたモジュールがあらかじめ豊富に揃っているおかげです。日々の利用では、その存在を意識することすらほとんどありませんが、裏では絶えずモジュールが着脱されているのです。

では、いつ自分でモジュールを作るのでしょうか。それは、Linuxがまだ対応していない独自設計のハードウェアを扱うときです。自社で起こした基板や、市販されていない独自のセンサのように、世の中に出回っていない機器には専用のドライバが存在しないため、自分で書く必要があります。組込み機器の開発では、この場面がしばしば訪れます。たとえば、学習でよく使われるRaspberry Piのような小型コンピュータでも、自分で組んだ回路上のLEDやスイッチを制御したいとなれば、そのためのモジュールを書くことになります。学習の場面では、まずメッセージを出すだけの小さなモジュールを作って組み込み・取り外しの流れを体で覚え、そこからGPIOにつないだLEDやスイッチの制御、さらにセンサとの通信へと段階的に広げていくのが定番です。最初は数行のモジュールから始められる手軽さも、学習の題材として優れている点です。「カーネルを再ビルドせずに機能を着脱できる単位」というカーネルモジュールの性格こそが、こうしたドライバ開発すべての土台になります。

この項目に出てくる用語

カーネルモジュールかーねるもじゅーる
あとからカーネルに着脱できる拡張機能。多くのドライバはこの形。
カーネル空間かーねるくうかん
カーネルやドライバが動く特権領域。アプリのユーザ空間とは分離される。

関連コマンド

lsmodmodinfouname

▶ 学習アプリでこの続きを学ぶ・演習する