CentOS 7でkernelモジュールを作成してロードした
Follow @venividivici830
Linuxカーネルは、ローダブルモジュールのサポートを有効にした場合、カーネルの機能の一部をモジュールとして、カーネルの動作中に動的に追加することができる。
デバイスドライバーは、カーネルモジュールとして作られることが多い。
デバイスドライバーを勉強するための前提知識として、カーネルモジュールを作成してロードしてみた。
OSは、CentOS Linux release 7.6.1810 (Core)。
作業時のOSの状態としては、CentOS-7-x86_64-Minimal-1810.isoから、デフォルトのパラメーターでインストールした直後。
任意のディレクトリーに、module_test.cとMakefileの2つのファイルを、同じディレクトリー下に作成。
module_test.c
Makefile
コンパイル。
koファイルが生成されていることを確認。
モジュールの挿入と削除。
printkで指定したメッセージが出力されていることを確認。
コンパイル時に下記のエラーが発生。
/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を確認すると、存在しない。
kernel-develをインストールすることで、/usr/src/kernels/3.10.0-957.el7.x86_64が作成される。
gccがインストールされていない場合
コンパイル時に下記のエラーが発生。
LinuxQuestions.org kernel devel package
実際には、/lib/modules/`uname -r`/kernel以下に配置される。
/usr/src/kernels/カーネルバージョンに、カーネルソースがインストールされる。
kernel-develをyumでインストールすると、自動的に作成される。
Linux教科書 LPICレベル2
カーネルモジュールでは標準Cライブラリーが使えないため、printfの代わりにprintkを使う。
カーネルモジュールで標準Cライブラリーが使えない理由は、以下のとおり。
カーネルモジュールは、カーネルの一部である。
カーネルの大部分はCで書かれているが、カーネルにおいては、printfなどの標準Cライブラリーの大部分が使えない。
これは、Cに「ホスト環境」と「フリースタンディング環境」の2種類の実行環境があり、カーネル(およびその一部であるカーネルモジュール)の実行環境が後者だからである。
「ホスト環境」は、OS上で動作することを前提としており、カーネルを使って標準ライブラリーが実装されている。言い換えると、OSが環境を用意してくれているからこそ、標準ライブラリーは動作する。
「フリースタンディング環境」は、OSを前提としておらず、OSが提供する機能が使えない。このため、標準ライブラリーが使えないわけである。
teratail カーネルがC言語で書かれているということが納得できません
$(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が参照される、という流れのようだ。
Linux Device Drivers, 3rd Edition Chapter 2. Building and Running Modules
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 ModulesLinux Device Drivers, 3rd Edition Chapter 2. Building and Running Modules
コメント
コメントを投稿