Linuxデバイスドライバを書いてみる(1)

この間、O'ReillyのLinux Device Driversを読んでみました。
忘れないうちにメモを取っておこうと思います。

最終的にはZYNQ上で動くドライバを書くことが目的ですが、今回はとりあえず仮想環境上で動くCentOS用のドライバを書いて基本的な開発方法を勉強していこうと思います。

動作環境

CentOS7

開発環境の構築

ビルドに必要な環境を準備します。

sudo yum install kernel-devel

ソースコード

サンプルコードtest.cの内容を下記に示します。

#include <linux/module.h>
#include <linux/init.h>

static int test_init(void){
    printk(KERN_ALERT "driver loaded\n");
    printk(KERN_ALERT "Hello World\n");
    return 0;
}

static int test_exit(void){
    printk(KERN_ALERT "driver unloaded\n");
}

MODULE_LICENSE("Dual BSD/GPL");
module_init(test_init);
module_exti(test_exit);

ビルド

下記にMakefileの内容を示します。

obj-m := test.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
make

するとtest.koが生成されます。
これがデバイスドライバ(カーネルモジュール)です。

デバドラのインストールとアンインストール

デバイスドライバのインストールとアンインストールは動的に可能で

sudo insmod test.ko
sudo rmmod test

でできます。
また

dmesg

すればprintkの内容が見れます。

lsmod

でインストールされているデバイスドライバを確認できます。

ソースコードの解説

MODULE_LICENSE("Dual BSD/GPL");

デバイスドライバを作るとき、ライセンス表記が必ず必要なようです。GPLライセンスでないとビルド時に警告メッセージが表示されます。
詳細はこちらに載っています。
Linux Kernel と GPL 関連

module_init(test_init);
module_exti(test_exit);

ドライバロード時アンロード時にそれぞれ呼び出される関数を指定します。

printk(KERN_ALERT "Hello World\n");

デバイスドライバカーネルモードで動くためprintfを使えません。そこで、カーネルで動くprintkを使います。「KERN_ALERT」はメッセージの優先度であり、これ以外にも幾つかあります。
こちらを参考に
printk.9

まとめ

最小限のデバイスドライバを作るのは簡単にできました。より規模が大きなドライバの開発の手順は基本的にこれと全く同じです。
ユーザーモードで動くCのソースコードとは違っているのでしばらく触れないとすっかり忘れてしまいそうです。
また、バグが合った場合カーネルが停止するのでPCを再起動しなければなりません。最悪、データが壊れたりなども考えられるので、そういったところがドライバ開発が難しいと言われているところなんでしょうか。

次回はシステムコールで呼び出される関数を実装していこうと思います。
taltalp.hatenablog.jp