🐷

[WSL2] 自作Linuxデバイスドライバをシェルから動かしてみる

2023/12/30に公開

前回の記事で自作したデバイスドライバのロード・アンロードができるところまではできたので、今回は実際にデバイスドライバをシェルムから動かすところまでやってみる。

以下の記事にやり方が詳しく書かれていたので参考させて頂いた。
https://qiita.com/iwatake2222/items/580ec7db2e88beeac3de

手順

デバイスドライバのソースを準備

今回は以下のようなソースのデバイスドライバを作成した。

MyDevice.c
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/sched.h>

#define DRIVER_NAME "MyDevice"
#define DRIVER_MAJOR 63

MODULE_LICENSE("GPL");

static int MyDevice_open(struct inode *inode, struct file *file)
{
    printk("MyDevice open\n");
    return 0;
}

static int MyDevice_close(struct inode *inode, struct file *file)
{
    printk("MyDevice close\n");
    return 0;
}

static ssize_t MyDevice_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
    printk("MyDevice read\n");
    return 1;
}

static ssize_t MyDevice_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
    printk("MyDevice write\n");
    return 1;
}

struct file_operations s_myDevice_fops = {
    .open    = MyDevice_open,
    .release = MyDevice_close,
    .read    = MyDevice_read,
    .write   = MyDevice_write,
};

static int MyDevice_init(void)
{
    printk("MyDevice init\n");
    register_chrdev(DRIVER_MAJOR, DRIVER_NAME, &s_myDevice_fops);
    return 0;
}

static void MyDevice_exit(void)
{
    printk("MyDevice exit\n");
    unregister_chrdev(DRIVER_MAJOR, DRIVER_NAME);
}

module_init(MyDevice_init);
module_exit(MyDevice_exit);

とりあえず練習用なのでopen,read,write,close内の処理はただログ出力するだけにした。
init時にコールしているregister_chrdevはキャラクタデバイスドライバを登録するための関数。
デバイスドライバには色々な種類があるが、キャラクタデバイスドライバというのはテキストや文字ベースの入出力を提供するデバイスドライバのこと。

DRIVER_MAJORは参考サイトと同様に63で登録した。この番号は後からデバイスファイルを作成する時に必要になる。

Makefileを準備

ビルドするためのMakefileは以下の内容。

Makefile
obj-m := MyDevice.o

all:
	make -C ../WSL2-Linux-Kernel M=$(shell pwd) modules
clean:
	make -C ../WSL2-Linux-Kernel M=$(shell pwd) clean

モジュールロード

実装が終わったらビルドをする。

$ make
make -C ../WSL2-Linux-Kernel M=/home/meloq/work/build_kernel/driver modules
make[1]: Entering directory '/home/meloq/work/build_kernel/WSL2-Linux-Kernel'
  CC [M]  /home/meloq/work/build_kernel/driver/MyDevice.o
  MODPOST /home/meloq/work/build_kernel/driver/Module.symvers
  CC [M]  /home/meloq/work/build_kernel/driver/MyDevice.mod.o
  LD [M]  /home/meloq/work/build_kernel/driver/MyDevice.ko
  BTF [M] /home/meloq/work/build_kernel/driver/MyDevice.ko
make[1]: Leaving directory '/home/meloq/work/build_kernel/WSL2-Linux-Kernel'

ビルドが終わったら作成したモジュールをロードして正しく登録されていることを確認する。

$ sudo insmod MyDevice.ko
$ cat /proc/devices | grep MyDevice
 63 MyDevice

63番にMyDeviceというデバイスが登録されていることが分かる。

デバイスファイルを作成

シェルからデバイスにアクセスするためにはデバイスファイルを登録する必要がある。
最初にmknodのコマンドを使いデバイスファイルを登録する。
第2引数のcはキャラクタデバイスドライバ、第3引数の63はデバイスドライバのソースのregister_chrdevで登録したメジャーナンバーを意味する。

この後、シェルから使用するのでchmodでアクセス権限の変更もしておく。

$ sudo mknod /dev/myDevice c 63 1
$ sudo chmod 666 /dev/myDevice
$ ls -la /dev | grep myDevice
crw-r--r--  1 root root  63,   1 Dec 30 23:28 myDevice

シェルから動かしてみる

それでは実際に作成したデバイスファイルを使用してシェルから動かしてみる。
"test"という文字を作成したデバイスファイルに対してリダイレクトする。

$ sudo echo "test" > /dev/myDevice
$ dmesg | tail -7
[ 3930.599299] MyDevice open
[ 3930.602283] MyDevice write
[ 3930.602443] MyDevice write
[ 3930.602539] MyDevice write
[ 3930.602624] MyDevice write
[ 3930.602747] MyDevice write
[ 3930.603215] MyDevice close

open,write,closeが実行されてデバイスドライバのソースで仕込んだログが出力されているのが分かる。

これだけだとreadが動いているのが確認できないので、以下を実行してreadも確認する。
readを見るためにはcatを実行すればよい。実行してみる。

$ cat /dev/myDevice

実行すると処理が実行されっぱなしになるのでCtrl-Cで中断する。
どうやら実行している間はreadが何度も呼ばれてしまうらしい。
ログで正しく動作したかを確認する。

$ dmesg | tail -5
[ 4311.962308] MyDevice read
[ 4311.962412] MyDevice read
[ 4311.962514] MyDevice read
[ 4311.962619] MyDevice read
[ 4311.962764] MyDevice close

とりあえずread処理も動いていたのでこれで自作デバイスドライバをシェルから使うところまでできた。次回はもう少し高度なドライバ作成にチャレンジしてみようと思う。

後、最後に以下のコマンドを実行してデバイスファイルとモジュールのアンロードをして後始末しておく。

$ sudo rm /dev/myDevice
$ sudo rmmod MyDevice.ko

Discussion