Linux内核模块

Linux内核模块是可动态加载到正在运行的内核中、用于扩展其功能(如添加硬件驱动或文件系统支持)的代码片段,无需重新编译或重启整个系统。

一个简单的模块
  1. test.c示例如下:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/stat.h>

// 声明模块的通用许可证 
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Nrvcer");
MODULE_DESCRIPTION("A simple Linux module");
MODULE_VERSION("0.1");


// 向内核模块传递参数
short myshort = 10 ;
// S_IRUGO表示用户,组,其他用户可以读
module_param(myshort, short, S_IRUGO);
MODULE_PARM_DESC(myshort, "A short integer");

int myint = 10 ;
module_param(myint, int, S_IRUGO);
MODULE_PARM_DESC(myint, "An integer");

long mylong = 10 ;
module_param(mylong, long, S_IRUGO);
MODULE_PARM_DESC(mylong, "A long integer");

char *mycharp = "hello" ;
module_param(mycharp, charp, S_IRUGO);
MODULE_PARM_DESC(mycharp, "A character pointer");

int myarray[4] = {1, 2, 3, 4};
int size = sizeof(myarray) / sizeof(int);
module_param_array(myarray, int, &size, S_IRUGO);
MODULE_PARM_DESC(myarray, "An array of integers");

// 模块初始化函数
// insmod xxx.ko  -> module_init() -> mymodule_init()
static int __init mymodule_init(void)
{
    printk(KERN_INFO "Hello world!\n");
    printk(KERN_INFO "myshort = %hd\n", myshort);
    printk(KERN_INFO "myint = %d\n", myint);
    printk(KERN_INFO "mylong = %ld\n", mylong);
    printk(KERN_INFO "mycharp = %s\n", mycharp);
    printk(KERN_INFO "myarray = %d, %d, %d, %d\n", myarray[0], myarray[1], myarray[2], myarray[3]);
    return 0;
}

// 模块退出函数
// rmmod xxx.ko  -> module_exit() -> mymodule_exit()
static void __exit mymodule_exit(void)
{
    printk(KERN_INFO "Goodbye world!\n");
}

module_init(mymodule_init);
module_exit(mymodule_exit);

  1. Makefile如下所示:
$(warning KERNELRELEASE=$(KERNELRELEASE))          		 # 打印提示,打印变量内的值
ifeq ($(KERNELRELEASE),)              

#KERNELDIR ?= /home/linux/emb2307/12-linuxsys/myir-imx-linux      #交叉开发内核路径

KERNELDIR ?= /lib/modules/$(shell uname -r)/build   # 本地开发, ubuntu22.04的路径,内核头文件目录

PWD := $(shell pwd)                     # 获取路径

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
	rm -rf *.o *~ core .depend .*.cmd *.mod *.mod.c .tmp_versions Module* modules*

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod *.mod.c .tmp_versions Module* modules*

.PHONY: modules modules_install clean

else
	MODULE_NAME ?= hello
    obj-m := ${MODULE_NAME}.o
endif

  1. 编译:sudo make MODULE_NAME=test
  2. 查看信息:
wzh@wzh:~/code$ sudo dmesg -c
wzh@wzh:~/code$ sudo insmod test.ko
wzh@wzh:~/code$ sudo dmesg
[ 2800.124737] test: loading out-of-tree module taints kernel.
[ 2800.124751] test: module verification failed: signature and/or required key missing - tainting kernel
[ 2800.128365] Hello world!
[ 2800.128371] myshort = 10
[ 2800.128374] myint = 10
[ 2800.128376] mylong = 10
[ 2800.128377] mycharp = hello
[ 2800.128379] myarray = 1, 2, 3, 4
wzh@wzh:~/code$ lsmod | grep test
test                   12288  0
wzh@wzh:~/code$ sudo rmmod test
wzh@wzh:~/code$ lsmod | grep test
wzh@wzh:~/code$ sudo dmesg
[ 2800.124737] test: loading out-of-tree module taints kernel.
[ 2800.124751] test: module verification failed: signature and/or required key missing - tainting kernel
[ 2800.128365] Hello world!
[ 2800.128371] myshort = 10
[ 2800.128374] myint = 10
[ 2800.128376] mylong = 10
[ 2800.128377] mycharp = hello
[ 2800.128379] myarray = 1, 2, 3, 4
[ 2877.839972] Goodbye world!

模块的相关操作
  1. 模块的安装
# 手动加载内核模块
insmod xxx.ko
# 动态加载内核模块
modprobe xxx.ko
  1. 查看模块信息:
# 显示后不清除信息
dmesg
# 显示后清除信息
dmesg -c
  1. 卸载模块:rmmod moduleName
  2. 查看系统中已经安装的模块:lsmod