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

カーネルモジュールとは

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

コマンドやアプリは、自分の力でハードウェアを動かしているわけではない。実際にハードウェアを握り、メモリの配分やプロセスの実行を取り仕切っているのは、Linuxの中心にある「カーネル」だ。

私たちが使うコマンドやアプリは、このカーネルにお願いをして仕事をしてもらう立場にすぎない。

🔗
たとえカーネルは会社の現場責任者。アプリは「これお願い」と頼む側で、機械(ハードウェア)に直接触れるのは責任者だけだ。

では、そのカーネルは世に出るすべての機器をあらかじめ知っているのか。そうではない。あとから機能を付け足したり外したりできる拡張のしくみがあり、その部品ひとつひとつをカーネルモジュールと呼ぶ。

デバイスを動かすためのソフトウェアであるデバイスドライバの多くは、このカーネルモジュールの形で作られる。ファイルとしては拡張子 .ko(kernel object の略)を持ち、必要になったときだけカーネルに組み込み、不要になれば取り外せる。

💡
ポイントドライバの多くはカーネルモジュール=拡張子 .ko のファイル。必要なときだけ差し込み、不要なら抜ける。

普段あなたが書くアプリが拡張子のない実行ファイルになるのとは別物だ、という点を最初に押さえておきたい。

ユーザ空間(アプリ・コマンド)アプリコマンドカーネル空間(中核プログラム)カーネルモジュール.ko = ドライバお願いするハードウェア制御

🧩 組み込み済みではなく、あとから差し込む形にする理由

カーネルモードで動くプログラムの作り方には、大きく分けて2つある。

ひとつは、ソースコードをカーネル本体と一体にしてコンパイル・ビルドする方法だ。これは確実に動くが、少し直すたびにカーネル全体を作り直し、そのうえで再起動しなければ反映されない。

カーネルのビルドは規模が大きく時間もかかるため、開発のたびにこれを繰り返すのは大きな負担だ。

もうひとつが、必要なときだけ部品のように差し込めるカーネルモジュールとして作る方法である。モジュールはカーネルとは別に用意しておき、稼働中のカーネルに後から組み込める。

カーネルを丸ごと作り直さずに機能を足したり引いたりできるため、ドライバの開発と動作確認をこの小さな単位で回せる。

本体は作り直さず、部品だけ差し替えるLinux カーネル本体(稼働中)止めずに機能を足したり外したりできるe1000e.ko有線NIC のドライバusbhid.koUSBキーボード等自作 hello.ko書いて試す最初の一歩insmod で差し込み rmmod で外す。書く→組み込む→ログを見る→外す、を短く回せる

書いて、組み込んで、ログを見て、外してまた直す。この短いサイクルを支えるのがモジュールという仕組みであり、だからこそドライバ開発の基本単位として使われる。

🗺 アプリとドライバは、動く「場所」が違う

モジュールを理解するうえで欠かせないのが、プログラムの動く「場所」の違いだ。

Linuxで動くプログラムは、アプリやコマンドなどが暮らすユーザ空間と、カーネル本体やドライバが動くカーネル空間に分かれている。CPU自身にユーザモードと特権モード(カーネルモードとも呼ぶ)という動作の段階が用意されていて、この2つの空間はそれぞれのモードに対応する。

ユーザ空間のプログラムはユーザモードで動き、自分に割り当てられたメモリの外には手を出せず、ハードウェアを直接操作することも許されていない。

なぜそこまで縛るのか。暴走したアプリが他のプログラムのデータを壊したり、勝手にハードウェアをいじったりするのを防ぐための、システムを守るしくみだからだ。一台のコンピュータを複数の人やプログラムが安全に共有できるのは、この分離があるからこそである。

一方でカーネル空間は特権モードで動き、ハードウェアへ直接アクセスできる強い権限を持つ。カーネルモジュールはこのカーネル空間で実行されるため、ユーザ空間のアプリには禁じられているハードウェア制御が可能になる。

ただし力が強いぶん責任も重い。モジュールの不具合は、アプリのように自分だけが落ちて済む話では終わらない。カーネルは全体でひとつのまとまりとして動いているため、ひとつのモジュールの誤りがカーネル全体を巻き込み、システムごと停止させてしまうこともある。

つまずきアプリの不具合は自分が落ちるだけ。だがモジュールの不具合はカーネルごと巻き込み、システム全体を止めることがある。

「強い権限の場所で動いている」という緊張感が、ドライバ開発では常について回る。この自覚が、次に見るアプリとの作りの違いにもつながっていく。

🎬 main 関数はどこへ消えたのか

普通のCプログラムは main 関数から始まり、プログラム自身が処理の流れを動かしていく。ところがカーネルモジュールには main 関数がない。

では、いつ・何をきっかけに動き出すのか。代わりに用意するのが、組み込まれた瞬間に一度だけ走る初期化関数と、取り外されるときに走る終了関数だ。

それらを「これが入口、これが出口」とカーネルに登録しておく。あとは適切なタイミングでカーネルの方から呼び出してもらう、という受け身の作りになる。

🔗
たとえモジュールは自分から動く主役ではなく、舞台監督(カーネル)が「今だ」と合図したときだけ出番が来る役者だ。

自分から動き出す主役ではなく、舞台監督であるカーネルが「今だ」と合図したときだけ出番が来る役者のようなものだ、と捉えると感覚がつかめる。

この発想の転換こそが、アプリ開発からドライバ開発へ移るときの最初の関門になる。

🔧 自分でモジュールを書く日はいつ来るのか

実際のLinuxには、すでに膨大な数のモジュールが用意されている。ネットワークカードを動かすドライバ、USB機器を扱うドライバといったものがモジュールとして提供されていて、機器をつないだときに必要なものが自動的に読み込まれる。

いま自分のシステムにどんなモジュールが組み込まれているかは lsmod というコマンドで一覧でき、個々のモジュールが誰によって作られ何をするものなのかは modinfo というコマンドで調べられる。

これらのモジュールは、カーネルのバージョンごとに決められた場所にまとめて置かれている。具体的には /lib/modules の下に、uname -r で得られるバージョン名のディレクトリがあり、その中だ。システムが状況に応じて適切なものを選び出す。

Linuxが世界中のさまざまなハードウェアに対応していて、インストール直後から多くの機器がそのまま使えるのは、こうしたモジュールがあらかじめ豊富に揃っているおかげだ。日々の利用では、その存在を意識することすらほとんどないが、裏では絶えずモジュールが着脱されている。

これだけ揃っているのに、いつ自分でモジュールを作るのか。それは、Linuxがまだ対応していない独自設計のハードウェアを扱うときだ。

自社で起こした基板や、市販されていない独自のセンサのように、世の中に出回っていない機器には専用のドライバが存在しないため、自分で書く必要がある。組込み機器の開発では、この場面がしばしば訪れる。

たとえば、学習でよく使われるRaspberry Piのような小型コンピュータでも、自分で組んだ回路上のLEDやスイッチを制御したいとなれば、そのためのモジュールを書くことになる。

学習の場面では、まずメッセージを出すだけの小さなモジュールを作って組み込み・取り外しの流れを体で覚え、そこからGPIOにつないだLEDやスイッチの制御、さらにセンサとの通信へと段階的に広げていくのが定番だ。最初は数行のモジュールから始められる手軽さも、学習の題材として優れている点である。

「カーネルを再ビルドせずに機能を着脱できる単位」というカーネルモジュールの性格こそが、こうしたドライバ開発すべての土台になる。その着脱を実際に行うコマンドを、次のトピックで手に取る。

この項目に出てくる用語

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

関連コマンド

lsmodmodinfouname

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