CentOS 7でkernelモジュールを作成してロードした



Linuxカーネルは、ローダブルモジュールのサポートを有効にした場合、カーネルの機能の一部をモジュールとして、カーネルの動作中に動的に追加することができる。
デバイスドライバーは、カーネルモジュールとして作られることが多い。
デバイスドライバーを勉強するための前提知識として、カーネルモジュールを作成してロードしてみた。

OSは、CentOS Linux release 7.6.1810 (Core)。
作業時のOSの状態としては、CentOS-7-x86_64-Minimal-1810.isoから、デフォルトのパラメーターでインストールした直後。

モジュールのコンパイル〜挿入・削除

必要なパッケージのインストール。
sudo yum -y install kernel-devel-$(uname -r)
sudo yum -y install gcc

任意のディレクトリーに、module_test.cとMakefileの2つのファイルを、同じディレクトリー下に作成。

module_test.c
#include <linux/module.h>

int test_init(void)
{
    printk("module init\n");
    return 0;
}

void test_exit(void)
{
    printk("module exit\n");
}

module_init(test_init);
module_exit(test_exit);

Makefile
obj-m := module_test.o

KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

default:
 $(MAKE) -C $(KDIR) M=$(PWD) modules

コンパイル。
make
make -C /lib/modules/3.10.0-957.el7.x86_64/build M=/home/myuser modules
make[1]: ディレクトリ `/usr/src/kernels/3.10.0-957.el7.x86_64' に入ります
  CC [M]  /home/myuser/module_test.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/myuser/module_test.mod.o
  LD [M]  /home/myuser/module_test.ko
make[1]: ディレクトリ `/usr/src/kernels/3.10.0-957.el7.x86_64' から出ます

koファイルが生成されていることを確認。
ls
Makefile        module_test.c   module_test.mod.c  module_test.o
Module.symvers  module_test.ko  module_test.mod.o  modules.order

モジュールの挿入と削除。
sudo insmod module_test.ko
sudo rmmod module_test.ko 

printkで指定したメッセージが出力されていることを確認。
dmesg
[ 1866.574980] module init
[ 1867.427225] module exit

エラー

kernel-develがインストールされていない場合
コンパイル時に下記のエラーが発生。
make
make -C /lib/modules/3.10.0-957.el7.x86_64/build M=/home/myuser modules
make: *** /lib/modules/3.10.0-957.el7.x86_64/build: そのようなファイルやディレクトリはありません.  中止.
make: *** [default] エラー 2

/lib/modules/3.10.0-957.el7.x86_64/buildディレクトリーを確認すると、/usr/src/kernels/3.10.0-957.el7.x86_64へのリンクになっている。

ls -ld /lib/modules/`uname -r`/build
lrwxrwxrwx. 1 root root 38  6月  2 11:43 /lib/modules/3.10.0-957.el7.x86_64/build -> /usr/src/kernels/3.10.0-957.el7.x86_64

/usr/src/kernels/3.10.0-957.el7.x86_64を確認すると、存在しない。
ls /usr/src/kernels/3.10.0-957.el7.x86_64
ls: /usr/src/kernels/3.10.0-957.el7.x86_64 にアクセスできません: そのようなファイルやディレクトリはありません

kernel-develをインストールすることで、/usr/src/kernels/3.10.0-957.el7.x86_64が作成される。
ls /usr/src/kernels/3.10.0-957.el7.x86_64
Kconfig         Module.symvers  block    firmware  init    lib  samples   sound  virt
Makefile        System.map      crypto   fs        ipc     mm   scripts   tools  vmlinux.id
Makefile.qlock  arch            drivers  include   kernel  net  security  usr

gccがインストールされていない場合
コンパイル時に下記のエラーが発生。
make
make -C /lib/modules/3.10.0-957.el7.x86_64/build M=/home/myuser modules
make[1]: ディレクトリ `/usr/src/kernels/3.10.0-957.el7.x86_64' に入ります
arch/x86/Makefile:96: stack-protector enabled but compiler support broken
arch/x86/Makefile:166: *** CONFIG_RETPOLINE=y, but not supported by the compiler. Compiler update recommended..  中止.
make[1]: ディレクトリ `/usr/src/kernels/3.10.0-957.el7.x86_64' から出ます
make: *** [default] エラー 2


kernel-devel

カーネルモジュールのコンパイルに必要なヘッダーを提供するパッケージ。

LinuxQuestions.org kernel devel package

/lib/modules、/usr/src/kernels

/lib/modules/`uname -r`以下には、カーネルモジュールが配置されている。
実際には、/lib/modules/`uname -r`/kernel以下に配置される。

/usr/src/kernels/カーネルバージョンに、カーネルソースがインストールされる。
kernel-develをyumでインストールすると、自動的に作成される。

Linux教科書 LPICレベル2 

printk

カーネルによって定義され、モジュールに対してエクスポートされた、printf関数の代わりとなる関数。
カーネルモジュールでは標準Cライブラリーが使えないため、printfの代わりにprintkを使う。

カーネルモジュールで標準Cライブラリーが使えない理由は、以下のとおり。

カーネルモジュールは、カーネルの一部である。
カーネルの大部分はCで書かれているが、カーネルにおいては、printfなどの標準Cライブラリーの大部分が使えない。
これは、Cに「ホスト環境」と「フリースタンディング環境」の2種類の実行環境があり、カーネル(およびその一部であるカーネルモジュール)の実行環境が後者だからである。
「ホスト環境」は、OS上で動作することを前提としており、カーネルを使って標準ライブラリーが実装されている。言い換えると、OSが環境を用意してくれているからこそ、標準ライブラリーは動作する。
「フリースタンディング環境」は、OSを前提としておらず、OSが提供する機能が使えない。このため、標準ライブラリーが使えないわけである。

teratail カーネルがC言語で書かれているということが納得できません

Makefile

obj-m := module_test.o は、module_test.oから生成される1つのモジュールがあることを定義する。

$(MAKE) -C $(KDIR) M=$(PWD) modules が動作するためには、カーネルビルドシステムのコンテキストで実行される必要がある。
このため、カーネルソースがインストールされたディレクトリー、/lib/modules/`uname -r`/build(/usr/src/kernels/3.10.0-957.el7.x86_64)を、-Cで指定する。
M=には、コンパイルするモジュールのソースが格納されたディレクトリーを指定する。
まず、-Cで指定したディレクトリーにあるMakefileが実行され、modulesターゲットがビルドされる前にM=で指定したディレクトリーに戻り、obj-mが参照される、という流れのようだ。

参考文献

Fedora DOCUMENTATION 7.7. Building Only Kernel Modules
Linux Device Drivers, 3rd Edition Chapter 2. Building and Running Modules


コメント

このブログの人気の投稿

PowerShell 6で、Shift_JISのCSVをImport-Csvで読み込んだら文字化けした

Windowsで、特定のユーザーに特定のサービスの再起動を許可する

PowerShellでイベントログを取得する時、「指定した選択条件に一致するイベントが見つかりませんでした。」が煩わしいのでcatchする