驱动是模块程序,模块程序不一定都是驱动
模块三要素
- 模块许可声明
- MODULE_LICENSE("GPL");
- 加载函数
- 默认是int init_module(void)
- 成功返回0
- 失败返回负数错误码
- 卸载函数
- 默认是void cleanup_module(void)
modules | app | |
运行空间 | 内核空间 | 用户空间 |
程序入口 | 加载函数 | main函数 |
资源管理 | 自己申请与回收 | 系统自动申请与回收 |
模块编译与运行的两个前提条件:
- 使用配置并编译后的内核源码
- 编译模块所用内核版本必须与运行模块时内核版本一致
模块编译
进入模块源码目录,执行make
Makefile 如下:
ifeq ($(KERNELRELEASE),)
#KERNELDIR ?= /home/lht/kernel2.6/linux-2.6.14
KERNELDIR ?= /lib/modules/$(shell uname -r)/buildPWD := $(shell pwd)
modules:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions modules* Module*
.PHONY: modules modules_install clean
elseobj-m := hello.ohello-objs := hello.c
endif格式obj-m := <finall_module>.o<finall_module>-m := <module1>.c <module2>.c ...
静态编译模块与动态编译模块
静态编译模块 | 动态编译模块 | |
编译方式 | 以<*>方式编译模块 | 以<M>方式编译模块 |
模块加载时间 | 在kernel启动时加载 | 需要时由用户加载 |
模块存放位置 | zImage中 | 以.ko方式存在模块 |
内部模块与外部模块
内部模块 | 外部模块 | |
编译方式 | 在kernel目录下 make module,动态编译 | 模块目录下 make,动态编译 |
模块编译位置 | 模块位于kernel源码内部 | 模块在kernel源码外部 |
加载方式 | modprobe,insmod | insmod |
卸载方式 | modprobe -r,rmmod | rmmod |
模块依赖度 | 会有模块依赖 | 很少有模块依赖 |
modinfo:intree | Y | 没有该项 |
modprobe 在加载与卸载时,能够根据kernel源码下的modules.dep,modules.dep.bin处理依赖
insmod,rmmod 不能处理模块依赖,需要手动解决模块依赖
modprobe在加载模块时,先加载依赖的模块,再加载指定的模块
modprobe在卸载模块时,如果当前模块没有被依赖,先卸载该模块,再检查依赖,如果其依赖没有被别的依赖,则该模块也被卸载。
模块代码格式
引入指定的kernel头文件#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>
MODULE_LICENSE("GPL");MODULE_AUTHOR("xufeng");MODULE_DESCRIPTION("hello device");MODULE_SUPPORTED_DEVICE("testdevice");
static int __init hello_init(void) {printk("hello world\n");return 0;}static void __exit hello_exit(void) {printk("goodbye world\n");}module_init(hello_init);module_exit(hello_exit);
#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>
指定加载模块的加载,卸载函数
module_init ();module_exit ();
__init关键字效果
静态加载时,添加到kernel的.init初始化段,之后不能被卸载动态加载时,无效果
__exit关键字效果
静态加载时,不编译,因为静态编译的模块不能卸载动态加载时,无效果
static关键字效果
防止函数名重复
添加模块详细信息
模块导出符号表MODULE_LICENSE("GPL");MODULE_AUTHOR("lvxinliang sciencelxl@gmail.com");
MODULE_DESCRIPTION("hello device");
MODULE_SUPPORTED_DEVICE("testdevice");
EXPORT_SYMBOL(符号名);EXPORT_SYMBOL(符号名);
Linux内核模块的程序结构
- 模块加载函数
- 模块卸载函数
- 模块许可声明
- 模块参数
- 模块导出符号
- 模块作者等信息声明
有关命令
查看内核打印信息
dmesg 显示消息
dmesg -c 清空消息
加载模块
insmod <模块名.ko>
modprobe <模块名>
列出所有加载的模块
lsmod
卸载模块
rmmod <模块名>
modprobe -r <模块名>
查看模块详细信息
modinfo <模块文件名>
查看内核符号表
cat /proc/kallsyms