Ubuntu12.04编译内核(三):Linux加载内核模块

mac2025-10-03  2

文章目录

一、Linux内核模块命令二、内核模块代码分析1.头文件2.模块初始化和退出函数3.模块参数的函数(1)宏描述(2)一般变量(3)字符串(4)数组 4.学术规范 三、实验1.任务2.过程(1)编写内核模块.c文件(2)编写Makefile文件(3)编译(4)安装模块:(5)查看`printk`的输出在缓冲区的信息:(6)卸载模块:(7)清除`printk`输出在缓存区的信息: 四、问题1.for循环初始化声明分开2.先卸载残留才能安装3.震惊!printk居然不能输出%c


一、Linux内核模块命令

内核模块的安装(加载): sudo insmod module_name.ko 如果有参数的话就是加上param_name=value:

int型:year=2020字符串:direction=embedded_system。 注意:都不行direction="embedded_system"和direction=embedded system和direction=embedded\ system数组:date_array=2020,3,9,用逗号分隔

内核模块的卸载: sudo rmmod module_name或modprobe -r module_name

查看系统已经加载的模块: lsmod

查看系统已经加载的模块信息: modinfo

查看printk输出在缓存区的信息: dmesg

清除printk输出在缓存区的信息: sudo dmesg -c

二、内核模块代码分析

1.头文件

// 模块 #include <linux/module.h> // 使用模块参数要的 #include <linux/moduleparam.h> // 内核 #include <linux/kernel.h>

2.模块初始化和退出函数

// module_init()内的初始化函数。 static int __init hello_init(void) { return 0; } // module_exit()内的退出函数。 static void __exit hello_exit(void) { } // 内核模块入口,相当于main()函数,完成模块初始化 module_init(hello_init); // 卸载时调用的函数入口,完成模块卸载 module_exit(hello_exit); module_init()和module_exit()内填的是具体实现初始化和退出时具体做什么的函数名字,比如hello_init和hello_exit(随便起)。

3.模块参数的函数

(1)宏描述

使用宏MODULE_PARM_DESC(variable_name, "xxx")对定义的参数进行说明

参数:

一般是static静态的

(2)一般变量

module_param(int_name, type, perm); module_param_named(ext_name, int_name, type, perm);

参数:

ext_name:(external)用户看到的参数名,对外的参数名int_name:(internal)当参数中没有ext_name的时候,此参数即是用户看到的参数名,又是模块内接受参数的变量。type:表示参数的数据类型的标识符byte, short, ushort, int, uint, long, ulong, charp(重点), bool, invbool charp是指向字符串的指针,而不是char类型,也不能填char。perm:指定了在sysfs中相应文件的访问权限。(一般不考虑,0或S_IRUSR即可)

(3)字符串

一种是charp的字符串指针形式,内外名一致另一种是string的字符串数字形式,内外名可异名

module_param_string(ext_name, string, len, perm);

参数:

ext_name:展现给用户插入是的参数名,MODULE_PARM_DESC()使用的是这个而不是stringstring:存储内容的字符串数组变量名len:字符串的长度,不像charp是动态的,这个是预先分配的,一般就填字符串数组的长度。perm:0

(4)数组

module_param_array(int_name, type, nump, perm); module_param_array_named(ext_name, int_name, type, nump, perm);

参数:

nump:系统动态判断出有多少个参数存放在数组中,然后将其写入一个整数变量的地址中,如&narr(类似scanf("%d",&narr))。 动态写入,不加static

4.学术规范

// 内核模块描述 MODULE_DESCRIPTION("a simple driver module"); // GPL协议证书 MODULE_LICENSE("GPL");

三、实验

1.任务

编写一个内核模块; 编译该模块; 加载、卸载该模块;

2.过程

(1)编写内核模块.c文件

mkdir project && cd project gedit catkinModule.c // 模块 #include <linux/module.h> // 使用模块参数要的 #include <linux/moduleparam.h> // 内核 #include <linux/kernel.h> /* 内外同名 */ // 一个int型的参数year static int year = -1; module_param(year, int, 0); MODULE_PARM_DESC(year, "内外同名的int型的整数"); /* 内外异名 */ // 一个int型的参数month,对外是month_ext static int month = -1; module_param_named(month_ext, month, int, 0); MODULE_PARM_DESC(month_ext, "内外异名的int型的整数"); /* 两种字符串 */ // 一个指针形式的字符串direction(charp) static char *direction = "default"; module_param(direction, charp, 0); MODULE_PARM_DESC(direction, "指针形式的字符串"); // 一个数组形式的字符串subject(string),对外是subject_ext #define LEN 10 static char subject[LEN]; module_param_string(subject_ext, subject, LEN, 0); MODULE_PARM_DESC(subject_ext, "数组形式的字符串"); /* 内外同名的数组 */ // 一个int型的数组date_array static int date_array[3]; // 用于保存数组int_array的实际元素个数 int narr; module_param_array(date_array, int, &narr, 0); MODULE_PARM_DESC(date_array, "int数组"); /* 内外异名的数组 */ // 一个int型的数组date_array2,对外是date_array2_ext static int date_array2[3]; // 用于保存数组int_array的实际元素个数 int narr2; module_param_array_named(date_array2_ext, date_array2, int, &narr2, 0); MODULE_PARM_DESC(date_array2_ext, "int数组"); // module_init()内的初始化函数。输出参数:int、字符串、int数组 static int __init hello_init(void) { printk(KERN_ALERT "Hello, my LKM.\n"); // 输出int型的参数year printk(KERN_ALERT "[int]year:%d.\n", year); // 输出int型的参数month,对外是month_ext printk(KERN_ALERT "[int]month:%d.\n", month); // 输出指针形式的字符串direction(charp) printk(KERN_ALERT "[charp]direction:%s.\n", direction); // 输出数组形式的字符串subject(string) printk(KERN_ALERT "[string]subject:%s.\n", subject); // 输出int型的数组date_array printk(KERN_ALERT "[array]date_array:\n"); int i; for (i = 0; i < narr; i++) { printk(KERN_ALERT "(%d) %d\n", i, date_array[i]); } // 输出int型的数组date_array2,对外是date_array2_ext printk(KERN_ALERT "[array]date_array2:\n"); int j; for (j = 0; j < narr2; j++) { printk(KERN_ALERT "(%d) %d\n", j, date_array2[j]); } return 0; } // module_exit()内的退出函数。输出再见 static void __exit hello_exit(void) { printk(KERN_ALERT "Bye, my LKM.\n"); } module_init(hello_init); module_exit(hello_exit); // 内核模块描述 MODULE_DESCRIPTION("a simple driver module"); // GPL协议证书 MODULE_LICENSE("GPL");

(2)编写Makefile文件

gedit Makefile obj-m := catkinModule.o KERNELDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions modules.order Module.symvers

(3)编译

make

PS: 如果重新编译的话,要把之前残留的垃圾删除(试过不管这些垃圾也能编译成功,但编辑结果还是原来的就很气)

# 清理垃圾 make clean # 编译 make

(4)安装模块:

先清理一下缓存,不然一会就可能输出一大堆多余东西,影响到我们想要看到的输出东西

sudo dmesg -c

安装

sudo insmod catkinModule.ko year=2020 direction=embeddedSystem subject_ext=software date_array=2020,3,10 date_array2_ext=2020,3,11

【可选部分】:

查看系统已经加载的模块: lsmod | grep catkinModule

查看系统已经加载的模块信息: modinfo catkinModule.ko

这是.ko文件,所以要在存在这个文件的目录(在这里就是当前编译的目录)下才能查看到。

(5)查看printk的输出在缓冲区的信息:

sudo dmesg

(6)卸载模块:

sudo rmmod catkinModule

(7)清除printk输出在缓存区的信息:

sudo dmesg -c

四、问题

1.for循环初始化声明分开

for (int i = 0; i < narr; i++)

会报错:

解决:要么使用c99,要么就注意点分开写

int i; for (i = 0; i < narr; i++)

2.先卸载残留才能安装

先卸载:

sudo rmmod module_name

3.震惊!printk居然不能输出%c

出来是\x17之类的十六进制结果
最新回复(0)