驱动基础——内核模块

本文使用环境:

内核版本:Linux 3.0.15 / iTop4412_Kernel_3.0

硬件平台:armv7 / itop-4412

编译环境:Ubuntu Linux 12.04 LTS / gcc version 4.4.1 (Sourcery G++ Lite 2009q3-67)

0. 引言

Linux内核架构很庞大,包含的组件也非常多,如果静态地将所有功能全部编译到内核,编译出的内核镜像文件将很大,且一旦有部分功能需要修改,每一次修改将不得不重新编译内核。Linux中支持以模块加载的方式动态地将包含特定功能的模块加载到内核,模块一旦被成功加载,就和内核中其他模块一样。模块机制分离了内核中相对固定的部分变化的部分,给调试带来了极大的便利,这种机制类似VxWorks中的模块加载机制。

1. linux内核模块

1.1 内核模块代码

下面给出一个最简单的内核模块simple_module.c

#include <linux/init.h>
#include <linux/module.h>

static int __init simple_module_init(void)
{
	printk(KERN_INFO "%s called\n", __FUNCTION__);

	return 0;
}

static int __exit simple_module_exit(void)
{
	printk(KERN_INFO "%s called\n", __FUNCTION__);

	return 0;
}

module_init(simple_module_init);
module_exit(simple_module_exit);

MODULE_AUTHOR("ryan");
MODULE_LICENSE("GPL v2");

1.2 内核模块组成

该模块主要包含了模块加载函数模块卸载函数模块GPL v2许可声明三大部分,其他部分如:MODULE_AUTHOREXPORT_DESCRIPTIONEXPORT_SYMBOLEXPORT_SYMBOL_GPL为可选项,非必须。

  1. 模块被加载时执行由module_init声明的模块加载函数simple_module_init
  2. 模块被卸载时执行由module_exit声明的模块卸载函数simple_module_exit
  3. MODULE_LICENSE("GPL v2");语句声明该模块使用的许可证类型,可用其他LICENSE"GPL""GPL and additional rights""Dual MIT/GPL""Dual BSD/GPL""Dual MPL/GPL""Proprietary"详情见官方文档。当模块中没有LICENSE声明时,加载模块将显示内核被污染的警告:
[root@iTOP-4412]# insmod simple_module.ko 
[ 2294.055272] simple_module: module license 'unspecified' taints kernel.
[ 2294.060472] Disabling lock debugging due to kernel taint
[ 2294.068879] simple_module_init

模块使用了printk函数,该函数用于内核空间数据输出,类似于用户空间中使用的printfprintk函数可以定义消息打印级别,如simple_module.c中使用的KERN_INFO宏,该宏定义在内核源码目录/include/linux/printk.h文件中定义,使用printk函数时可根据实际情况选择不同的打印级别:

image-20201020103538204

1.3 修改Makefile

要将simple_module.c文件编译成模块还需要linux内核源码,本文使用的内核版本为linux 3.0.15,在当前simple_module.c源码路径使用如下Makefile文件编译simple_module.c,该Makefile将调用内核目录中的Makefile完成simple_module模块编译

#!/bin/bash
#将simple_module.c这个文件编译成中间文件simple_module.o
obj-m += simple_module.o 

#linux内核源码路径
KDIR := /home/topeet/android4.0/iTop4412_Kernel_3.0

#当前目录变量
PWD ?= $(shell pwd)

#make命名默认寻找第一个目标
#make -C就是指调用执行的路径
#$(KDIR)Linux源码目录,当前环境指的是/home/topeet/android4.0/iTop4412_Kernel_3.0
#$(PWD)当前目录变量
#modules要执行的操作
all:
	make -C $(KDIR) M=$(PWD) modules
#make clean执行的操作是删除后缀为o的文件
clean:
	rm -rf *.o *.ko *.mod.c *.order *.symvers

1.4 模块编译

Makefile修改完成后,在simple_module.c源码路径下执行make命令编译模块,编译完成后将在simple_module.c源码路径下产生simple_module.ko文件,该文件即待加载的模块文件

kernel_module_make

当前itop4412开发板使用NFS根文件系统启动,将simple_module.ko文件拷贝到根文件系统目录中,开发板上电后自动挂载NFS根文件系统,进入相应的目录即可访问simple_module.ko文件。

image-20201019230305794

1.5 使用insmod/rmmod命令加载/卸载模块

启动开发板,进入/system/drvbin/路径执行insmod simple_module.ko安装模块:

image-20201019231329654

模块加载函数simple_module_init被调用。

执行rmmod simple_module卸载模块:

image-20201019231715466

模块卸载函数simple_module_exit被调用。

模块加载后可通过lsmod命令查看已加载的模块,执行cat /proc/modules命令也可以得到相同的结果。

image-20201019233315132

1.6 使用modprobe/modprobe -r命令加载/卸载模块

模块的加载和卸载也可以使用modprobe <模块名>modprobe -r <模块名>命令,使用modprobe命令加载模块时将同时加载该模块所依赖的模块,使用modprobe -r的方式卸载模块时也会同时卸载该模块所依赖的模块

使用modprobe命令需要先将模块文件拷贝到/lib/modules/'uname -r'路径下,在当前环境下'uname -r'返回的结果为3.0.15

image-20201019234615489

modprobe的探测路径为/lib/modules/3.0.15/,该路径下还需要一个modules.dep文件,不存在这个文件时用touch modules.dep命令新建一个即可,将simple_module.ko模块文件拷贝到/lib/modules/3.0.15/路径下:

image-20201019235133534

经过前面的操作,执行modprobe命令的条件已经具备,执行modprobe simple_module(不需要文件扩展名)命令加载模块:

image-20201019235809314

执行modprobe -r simple_module命令卸载模块:

image-20201019235929006

使用modprobe [-r]命令和insmod/rmmod命令得到的结果并无二致。

2. 小结

本文首先以linux内核模块必要组成部分的角度,给出一个简单的内核模块代码,接着分析一个内核模块的基本组成,并简要介绍了内核模块的编译方法,最后通过两组命令描述内核模块加载与卸载的方法。

参考

  • 《Linux设备驱动开发详解-基于最新的Linux4.0内核》第4章
  • 《itop-4412开发板之精英版使用手册_v4.0》第11章
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值