应用层的进程是通过内核层驱动来访问硬件的,Linux内核源码在每次编译之后生成一个总的镜像,将镜像加载内存中运行并使用(内核在系统运行时会移植占用内核空间)。驱动属于内核源码的一部分,如果每次修改驱动都要重新编译加载内核的话,这态麻烦了,所以我们通过模块,使驱动可以独立于内核镜像之外,并能动态的加载和卸载。

在搭建好开发环境之后,通过source insight我们可以方便的查看和编辑内核源码,并结合交叉编译工具链实现编辑--编译--烧录--执行 的完整开发流程。

  • 物理机(Win7)     程序编辑环境
  • 虚拟机(Ubuntu14.04) 程序的编译环境
  • 开发板(FS4412)  程序的调试运行环境

 

编写驱动

创建驱动文件hello.c,编辑源码

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


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 <linux/init.h>                 包含模块装卸载函数原型声明
#include <linux/module.h>               这里没有用到其实
#include <linux/stat.h>                  包含printk函数的原型声明

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");       一种开源声明
 

 

编辑Makefile的规则

用来负责驱动文件的编译和管理。这里make -C参数,借用了内核源码根目录中的Makefile文件的规则来编译驱动代码生成成.ko模块。

ROOTFS_DIR = /nfs/rootfs

ifeq ($(KERNELRELEASE), )

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 xxx.ko   装载
  • rmmod xxx       卸载
  • modinfo         查看

 

其他

对模块的传参

有时候对原厂开发,一些代码叫做固件,我们不能进入函数的内部去修改参数的默认指定。通过命令行传参

  • 手动传递

insmod hello.ko myname="george" myvalue=33

  • 函数接收

 module_param(name, type, perm)

 

符号表

使用 EXPORT_SYMBOL 可以将一个函数以符号的方式导出给其他模块使用。原理类似于应用层的动态库。 这样就使模块与模块之间形成依赖关系(使用方依赖于导出符号的模块),例如

EXPORT_SYMBOL(my_add);   my_add是导出的函数名

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

int my_add(int a, int b)
{
	return a+b;
}

EXPORT_SYMBOL(my_add);


MODULE_LICENSE("GPL");