linux进阶:内核模块
现代内核派系
宏内核
关键功能(基本功能,不可裁剪、扩展)和服务功能(如文件系统、设备驱动、网络服务等,可裁剪、扩展)均在内核空间提供。运行效率高。扩展性较差。system call(系统调用)能够先入内核态来使用内核提供的服务。
微内核
内核空间只提供关键功能,服务功能在用户空间提供。运行效率较低。安全性、扩展性较高。

内核模块
为解决linux内核可扩展性和可维护性相对较差的缺陷。
内核模块头文件
1 #include <linux/init.h> /*包含module_init()和module_exit()函数的声明*/
2 #include <linux/module.h> /*包含内核模块信息声明的相关函数*/
3 #include <linux/kernel.h> /*包含内核提供的各种函数,如printk*/
内核模块加载与卸载
加载内核模块:insmod
卸载内核模块:rmmod
内核模块出入口
module_init():加载模块时该函数自动执行,进行初始化操作
module_exit():卸载模块时该函数自动执行,进行清理操作
内核模块信息声明
MODULE_LICENSE():表示模块代码接受的软件许可协议,Linux内核遵循GPL V2开源协议,内核模块与linux内核保持一致即可。
MODULE_AUTHOR():描述模块的作者信息。
MODULE_DESCRIPTION():对模块的简单介绍。
MODULE_ALIAS():给模块设置一个别名。
实验环境(野火为例)
开发板烧录好Debian镜像。启动开发板,搭建好nfs客户端,挂载共享文件夹。获取Debian镜像的内核源码并编译。(选择4.19.71版本内核,内核模块的功能需要依赖内核提供的各种底层接口。)
注:cat /etc/issue查看镜像日期。新版内核是4.19.35版本(22年之后),看驱动文档的“驱动章节实验环境搭建”。
下载linux内核源码(基于野火linux开发板,ebf-buster-linux存放着内核相关)
git clone https://github.com/Embedfire/ebf-buster-linux.git git clone https://gitee.com/Embedfire/ebf-buster-linux.git
安装必要环境工具库(make工具、gcc交叉编译链、gcc编译工具、bison语法分析器、flex词法分析器、libssl-dev OpenSSL通用库、lzop LZO压缩库的压缩软件)
sudo apt install make gcc-arm-linux-gnueabihf gcc bison flex libssl-dev dpkg-dev lzop
前往ebf-buster-linux目录,执行脚本,一键编译内核
sudo ./make_deb.sh
获取编译出来的内核相关文件
/home/pi/build
make_deb.sh如下:
1 deb_distro=bionic 2 DISTRO=stable 3 build_opts="-j 6" 4 build_opts="${build_opts} O=/home/pi/build" //O执行编译文件的存放路径 5 build_opts="${build_opts} ARCH=arm" 6 build_opts="${build_opts} KBUILD_DEBARCH=${DEBARCH}" 7 build_opts="${build_opts} LOCALVERSION=-imx-rl" 8 build_opts="${build_opts} KDEB_CHANGELOG_DIST=${deb_distro}" 9 build_opts="${build_opts} KDEB_PKGVERSION=l${DISTRO}" 10 build_opts="${build_opts} CROSS_COMPILE=arm-linux-gnueabihf-" 11 build_opts="${build_opts} KDEB_SOURCENAME=linux-upstream" 12 make ${build_opts} npi_v7+defconfig 13 make ${build_opts} 14 make ${build_opts} bindeb-pkg
实验一
helloworld.c文件
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/kernel.h> 4 5 static int __init hello_init(void) //__init指将这段函数指定保存在__init段内存 6 { 7 printk(KERN_EMERG "[ KERN_EMERG ] Hello World Module Init\n"); 8 printk("[ default ] Hello World Module Init\n"); 9 return 0; 10 } 11 12 static int __exit hello_exit(void) //__exit指将这段函数指定保存在__exit段内存 13 { 14 printk("[ default ] goodbye\n"); 15 } 16 17 module_init(hello_init); //hello_init作为模块函数入口 18 module_exit(hello_exit); //hello_exit作为模块函数出口 19 20 MODULE_LICENSE("GPL2"); //表示模块代码接受的软件许可协议 21 MODULE_AUTHOR("couvrir"); //描述模块的作者信息 22 MODULE_DESCRIPTION("hello world module"); //对模块的简单介绍 23 MODULE_ALIAS("test module"); //给模块设置一个别名
Makefile文件
1 KERNEL_DIR=/home/pi/build 2 3 ARCH=arm //架构 4 CROSS_COMPILE=arm-linux-gnueabihf- //编译链 5 export ARCH CROSS_COMPILE //将架构和编译链导出子Makefile 6 7 obj-m:=helloworld.o 8 all: 9 $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules 10 //整行意思是去子Makefile那里make modules这个伪目标 11 12 .PHONE:clean copy 13 clean: 14 $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean 15 copy: 16 sudo cp *.ko /home/couvrir/workdir //共享文件夹