应用层的进程是通过内核层驱动来访问硬件的,Linux内核源码在每次编译之后生成一个总的镜像,将镜像加载内存中运行并使用(内核在系统运行时会占用内核空间)。驱动属于内核源码的一部分,如果每次修改驱动都要重新编译加载内核的话,这太麻烦了,所以我们通过模块,使驱动可以独立于内核镜像之外,并能动态的加载和卸载。
在搭建好开发环境之后,通过source insight我们可以方便的查看和编辑内核源码,并结合交叉编译工具链实现编辑--编译--烧录--执行 的完整开发流程。
#include
#include
#include
static int __init hello_drv_init(void)
{
printk("-------%s-------------\n", __FUNCTION__);
return 0;
}
static void __exit hello_drv_exit(void)
{
printk("-------%s-------------\n", __FUNCTION__);
}
module_init(hello_drv_init);
module_exit(hello_drv_exit);
MODULE_LICENSE("GPL");
这个简单的驱动模块代码,没有涉及到任何硬件,它的功能是可以在加载和卸载该模块时打印输出信息,仅此。要建立这个框架,分成4个步骤:
1.加载头文件
#include
#include
#include
2.驱动模块装载/卸载函数声明
module_init(hello_drv_init); 装载 hello_drv_init为回调函数
module_exit(hello_drv_exit); 卸载 hello_drv_exit为回调函数
3.实现函数入口(回调函数实现)
static int __init hello_drv_init(void)
{
return 0;
}
static void __exit hello_drv_exit(void)
{
return ;
}
4.GPL声明
MODULE_LICENSE("GPL"); 一种开源声明
用来负责驱动文件的编译和管理。这里make -C参数,借用了内核源码根目录中的Makefile文件的规则来编译驱动代码生成成.ko模块。
ROOTFS_DIR = /nfs/rootfs
ifeq ($(KERNELRELEASE), )
#利用当前系统的内核中的Makefile进行编译
#KERNELDIR:= /lib/modules/$(shell uname -r)/build/
#或是指定内核源码的根目录
KERNEL_DIR = /mnt/hgfs/sharefolder/kernel/linux-3.14-fs4412
CUR_DIR = $(shell pwd)
all :
make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
clean :
make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
install:
cp -raf *.ko $(ROOTFS_DIR)/drv_module
else
obj-m += hello.o
endif
ROOTFS_DIR变量指定 nfs共享根目录
KERNEL_DIR变量指定 内核源码的根目录(绝对路径)
obj-m += hello.o 表示将hello.o这个目标文件编译成模块
Ubuntu系统中进入Makefile所在目录
linux@linux:/mnt/hgfs/sharefolder/kernel/linux-3.14-fs4412/drivers/mydrivers/hello_drv$ make
linux@linux:/mnt/hgfs/sharefolder/kernel/linux-3.14-fs4412/drivers/mydrivers/hello_drv$ make install
在内核运行时
有时候对原厂开发,一些代码叫做固件,我们不能进入函数的内部去修改参数的默认指定。通过命令行传参
insmod hello.ko myname="george" myvalue=33
module_param(name, type, perm)
使用 EXPORT_SYMBOL 可以将一个函数以符号的方式导出给其他模块使用。原理类似于应用层的动态库。 这样就使模块与模块之间形成依赖关系(使用方依赖于导出符号的模块),例如
EXPORT_SYMBOL(my_add); my_add是导出的函数名
//math.c
#include
#include
int my_add(int a, int b)
{
return a+b;
}
EXPORT_SYMBOL(my_add);
MODULE_LICENSE("GPL");