嵌入式Linux学习笔记(一)内核模块编程
1.总结
从事嵌入式行业多年,虽然因为工作原因接触过嵌入式Linux,也参与过相关产品的底层和应用功能开发,但对于嵌入式Linux的内核,驱动,以及上层开发,仍然停留在看的懂,基本会用的水平,没有过系统深入的去总结,随着工作年限的递增,越来越感受到这种浮躁感带来的技术面瓶颈。既然发现了问题,自然就要去解决,回想起我踏入嵌入式行业来的经历,正是对STM32芯片以及网络部分的学习总结笔记支撑我走到如今的地步,那么沉淀下来,从嵌入式Linux驱动开发开始,对嵌入式Linux进行系统的学习总结也是最符合我目前现状的解决办法,这也是我下定决心放弃日常娱乐,开始本系列的由来。
嵌入式Linux的掌握学习是很复杂的过程,从最基础的Linux安装,shell指令的学习和应用,交叉编译环境搭建,C语言开发,Linux内核接口,Linux系统接口,在掌握了前面所有知识后,才只是完成了产品开发的基础构建,这些知识不仅对于学习是难点,对于已经掌握的人来用文字描述清楚,特别是系统/软件版本引发的编译,调试问题,在学习以及开发中遇到的问题会抽空整理出来,在本篇中也会涉及部分。如果不理解,要善于使用搜索引擎,嵌入式Linux开发遇到的问题基本都能找到答案,一定要切记。不多说了,下面开始正篇的说明。
2. 内核模块实现
嵌入式Linux的驱动都是以模块的形式出现,掌握内核模块的实现是必须的。其实简单来说,Linux定义内核模块需要实现的接口,开发者只要按照规则用C语言实现这些需要的接口,在按照一定的规则编译,就可以使用系统指令lsmod/rmmod来说加载和移除自定义的模块,这套规则就是我们掌握内核模块需要学习的知识,按照功能分为以下接口:
必须模块
模块加载函数:module_init(func)
模块卸载函数: module_exit(func)
模块许可声明:MODULE_LICENSE(“str”)
支持的许可有: "GPL", "GPL V2", "GPL and additional right", "Dual BSCD/GPL", "DUAL MPL/GPL", "Proprietary"
可选模块
模块参数 -- 模块加载时传递变量 module_param(name, charp, S_IRUGO);
模块导出符号 --用于将符号导出,用于其它内核模块使用。
EXPORT_SYSMBOL(func)/EXPORT_SYSMBOL_GPL(func)
Linux内核2.6增加了函数校验机制,后续模块需要引入时要在Module.symvers下添加导入函数内核的路径和symbol。
模块作者 -- MODULE_AUTHOR(“str”)
模块描述 -- MODULE_DESCRIPTION(“str”)
模块版本 -- MODULE_VERSION(“str”)
模块别名 -- MODULE_ALIAS(“str”)
模块设备表 -- MODULE_DEVICE_TABLE, 对于USB或者PCI设备需要支持,表示支持的设备,目前不在详细描述。
在了解上述模块的基础上,就可以实现如下的模块代码:
//hello.ko#include <linux/init.h>
#include <linux/module.h>
//extern int add_integar(int a, int b);
staticchar *buf = "driver";
module_param(buf, charp, S_IRUGO); //模块参数
staticint __init hello_init(void)
{
int dat = 3; //int dat = add_integar(5, 6);
printk(KERN_WARNING "hello world enter, %s, %d
", buf, dat);
return0;
}
module_init(hello_init); //模块加载函数
staticvoid __exit hello_exit(void)
{
printk(KERN_WARNING "hello world exit
");
}
module_exit(hello_exit); //模块卸载函数
MODULE_AUTHOR("ZC"); //模块作者
MODULE_LICENSE("GPL v2"); //模块许可协议
MODULE_DESCRIPTION("a simple hello module"); //模块许描述
MODULE_ALIAS("a simplest module"); //模块别名
使用Makefile文件如下:
ifeq ($(KERNELRELEASE),)KDIR :
= /lib/modules/$(shell uname -r)/buildPWD :
= $(shell pwd)modules:
$(MAKE)
-C $(KDIR) M=$(PWD) modulesmodules_install:
$(MAKE)
-C $(KDIR) M=$(PWD) modules_installclean:
rm -rf *.o *.ko .depend *.mod.o *.mod.c modules.*.PHONY:modules modules_install clean
elseobj
-m :=hello.oendif
保存后,使用Make即可编译,如果遇到编译错误,请先查看文章最后的备注,未包含问题请搜索或者留言,编译结果如图所示。
之后执行指令modinfo hello.ko即可查看当前的模块信息。
如果无法查看信息,可通过dmesg查看加载信息。
2.内核模块的跨模块调用
上一节可以解决我们遇到的大部分内核实现问题,但某些时候我们可能需要一些公共内核模块,提供接口给大部分模块使用,这就涉及到内核模块的跨模块调用。
对于跨核模块调用的实现,对于调用的模块,主要包含2步:
1、 在代码实现中添加extern int add_integar(int a, int b);
2、 在编译环境下修改Module.symvers, 添加被链接模块的地址,函数校验值(可通过查看被链接模块编译环境下的Module.symvers内复制即可)
对于被链接的模块,代码实现如下:
//math.ko#include <linux/init.h>
#include <linux/module.h>
staticint __init math_init(void)
{
printk(KERN_WARNING "math enter
");
return0;
}
module_init(math_init);
staticvoid __exit math_exit(void)
{
printk(KERN_WARNING "math exit
");
}
module_exit(math_exit);
int add_integar(int a, int b)
{
return a+b;
}
EXPORT_SYMBOL(add_integar);
int sub_integar(int a, int b)
{
return a-b;
}
EXPORT_SYMBOL(sub_integar);
MODULE_LICENSE("GPL V2");
编译Makefile同上,需要将obj-m :=hello.o修改为obj-m :=math.o
执行make编译完成该文件,并通过insmod加载完模块后,可通过
grep integar /proc/kallsyms 查看加载在内核中的符号,状态如下:
然后加载insmod hello.ko, 即可跨文件调用该接口。如此,便初步完成对Linux内核模块的学习。
本文参考资料:
1. 《Linux设备驱动开发详解:基于最新的Linux4.0内核》 第四章
备注:
下面讲述编译遇到的问题和解决方案。
1.内核编译名称必须为Makefile,否则编译会出错
make[2]: *** No rule to make target `/usr/kernel/hello/Makefile". Stop.
make[1]: *** [_module_/usr/kernel/hello] Error 2
make[1]: Leaving directory `/usr/src/linux-headers-3.5.0-23-generic"
2.Makefile的内容,如果编译多个文件obj-m :=hello.o test.o
3.Makefile中,指令必须以Tab对齐,否则编译会异常。
4.printk不打印,一般来说输出的KERNEL_INFO为超过最大输出值,可直接通过dmesg,在系统信息内查看。
5.内核跨文件访问接口
除EXPORT_SYSMBOL外,在编译时Module.symvers需要包含对应函数的校验值,路径
0x13db98c9 sub_integar /usr/kernel/math/math EXPORT_SYMBOL
0xe1626dee add_integar /usr/kernel/math/math EXPORT_SYMBOL
否则编译时报警告
WARNING: "add_integar" [/usr/kernel/hello/hello.ko] undefined!
安装模块时出错
[ 9091.025357] hello: no symbol version for add_integar
[ 9091.025360] hello: Unknown symbol add_integar (err -22)
以上是 嵌入式Linux学习笔记(一)内核模块编程 的全部内容, 来源链接: utcz.com/z/515711.html