模块的加载
通常来说,在驱动模块的开发阶段,一般是将模块编译成.ko文件,再使用
sudo insmod module.ko
或者
depmod -a
modprobe module
将模块加载到内核,相对而言,modprobe要比insmod更加智能,它会检查并自动处理模块的依赖,而insmod出现依赖问题时仅仅是告诉你安装失败,自己想办法吧。
将模块编译进内核
这一章节我们并不关注模块的运行时加载,我们要讨论的是将模块编译进内核。
在学习内核的Makefile规则的时候就可以知道,将驱动程序编译成模块时,只需要使用:
obj-m += module.o
指定相应的源代码(源代码为module.c)即可,所以很多朋友就简单地得出结论:如果要将模块编译进内核,只要执行下面的的指令就可以了:
obj-y += module.o
事实上,这样是行不通的,要明白怎么将驱动程序编译进内核,我们还是得先了解linux源码的编译规则。
关于linux源码的编译规则和部分细节可以查看我的另一篇linux内核Makefile概览
本篇博客的所有实验基于arm平台,beagle bone开发板,内核版本为4.14.79
编译平台
注:在以下的讨论中,目标主机和本机指加载运行驱动程序的机器,是开发的对象。而开发机指只负责编译的机器,一般指PC机。
本机编译
在对驱动程序进行编译时,一般会有两种不同的做法:
- 直接在目标主机上编译
- 在其他平台上构建交叉编译环境,一般是在PC机上编译出可在目标板上运行的驱动程序
直接在目标主机上编译是比较方便的做法,本机编译本机运行。
通常,本机系统中一般不会自带linux内核源码的头文件,我们需要做的就是在系统中安装头文件:
sudo apt-get install linux-headers-$(uname -r)
$(uname -r)获取当前主机运行的linux版本号。
有了头文件,那么源代码从哪里来呢?答案是并不需要源代码,或者说是并不需要C文件形式的源代码,而是直接引用当前运行的镜像,在编译时,将/boot/vmlinuz-$(version)镜像当成库文件进行链接即可。
值得注意的是,/boot/vmlinuz-$(version)是linux启动时读取的镜像,但是在本机中进行驱动程序编译的时候并不会影响到这个镜像,换句话说,即使是指定了obj-y,驱动程序也不会编译到/boot/vmlinuz-$(version)镜像中,自然达不到将驱动编译进内核的效果。
交叉编译
本机(目标机)编译是比较方便的,但是无法改变生成的镜像,当然也可以将源码下载到本机(目标机)中进行编译,就可以生成相应的linux镜像。
但是一般情况下,在嵌入式开发中,不论是网络、内存还是执行速率,目标主机的性能一般不会太高,如果需要编译完整的源码时,用户会更倾向于在PC端构建编译环境以获取更好的编译性能。
选择在开发机上编译而不是本机编译时,需要注意的一点就是:通常嵌入