注:关于驱动的编译环境,在系统移植篇已详细讲解与安装
一、向内核添加新功能
1.1 静态加载法
即新功能源码 与内核 其它代码一起编译进uImage文件内,下面举例说明。
- 新功能源码与Linux内核源码在同一目录结构下,在
linux-3.14/drivers/char/ 目录下编写myhello.c ,文件内容如下:
#include <linux/module.h>
#include <linux/kernel.h>
int __init myhello_init(void)
{
printk("#####################################################\n");
printk("#####################################################\n");
printk("#####################################################\n");
printk("#####################################################\n");
printk("myhello is running\n");
printk("#####################################################\n");
printk("#####################################################\n");
printk("#####################################################\n");
printk("#####################################################\n");
return 0;
}
void __exit myhello_exit(void)
{
printk("myhello will exit\n");
}
MODULE_LICENSE("GPL");
module_init(myhello_init);
module_exit(myhello_exit);
- 给新功能代码配置Kconfig
cd ~/linux-3.14/drivers/char
vim Kconfig
config MY_HELLO
tristate "This is a hello test"
help
This is a test for kernel new function
- 改写myhello.c同级目录下的Makefile
cd ~/linux-3.14/drivers/char
vim Makefile
obj-$(CONFIG_MY_HELLO) += myhello.o
- make menuconfig 界面里将新功能对应的那项选择成
<*>
cd ~/linux-3.14
make menuconfig
6. make uImage 7. cp arch/arm/boot/uImage /tftpboot 8. 启动开发板观察串口终端中的打印信息
1.2 动态加载法
即新功能源码与内核其它源码不一起编译,而是独立编译成内核的插件(被称为内核模块)文件.ko
1.2.1 文件制作方法
方法1:新功能源码与Linux内核源码在同一目录结构下时
- 给新功能代码配置Kconfig(同静态配置)
- 给新功能代码改写Makefile(同静态配置)
- make menuconfig 界面里将新功能对应的那项选择成
<M> make uImage - cp arch/arm/boot/uImage /tftpboot
- make modules
make modules会在新功能源码的同级目录下生成相应的同名.ko文件(生成的ko文件只适用于开发板linux) 注意:此命令执行前,开发板的内核源码已被编译
方法2:新功能源码与Linux内核源码不在同一目录结构下时
- cd ~/linux-3.14
- mkdir mydrivercode
- cd mydrivercode
- cp …/linux-3.14/drivers/char/myhello.c .
- 添加Makefile文件,内容放在步骤末尾
- vim Makefile
- make (生成的ko文件适用于主机ubuntu linux)
- make ARCH=arm (生成的ko文件适用于开发板linux,注意此命令执行前,开发板的内核源码已被编译)
ifeq ($(KERNELRELEASE),)
ifeq ($(ARCH),arm)
KERNELDIR ?= /home/linux/linux-3.14
ROOTFS ?= /opt/4412/rootfs
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
endif
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install
clean:
rm -rf *.o *.ko .*.cmd *.mod.* modules.order Module.symvers .tmp_versions
else
obj-m += myhello.o
endif
#file命令可以查看指定ko文件适用于哪种平台,用法:
file ko文件
#结果带x86字样的适用于主机ubuntu linux,带arm字样的适用于开发板linux
1.2.2 文件使用
使用1:主机ubuntu下使用ko文件
sudo insmod ./文件名.ko
lsmod
sudo rmmod 文件名
sudo dmesg -C
dmesg
使用2:开发板Linux下使用ko文件
insmod ./文件名.ko
lsmod
rmmod 文件名
内核随时打印信息,我们可以在串口终端界面随时看到打印信息,不需要dmesg命令查看打印信息
二、内核模块基础代码解析
Linux内核的插件机制——内核模块
类似于浏览器、eclipse这些软件的插件开发,Linux提供了一种可以向正在运行的内核中插入新的代码段、在代码段不需要继续运行时也可以从内核中移除的机制,这个可以被插入、移除的代码段被称为内核模块。
主要解决:
- 单内核扩展性差的缺点
- 减小内核镜像文件体积,一定程度上节省内存资源
- 提高开发效率
- 不能彻底解决稳定性低的缺点:内核模块代码出错可能会导致整个系统崩溃
内核模块的本质:一段隶属于内核的“动态”代码 ,与其它内核代码是同一个运行实体,共用同一套运行资源,只是存在形式上是独立的。
#include <linux/module.h>
#include <linux/kernel.h>
int __init myhello_init(void)
{
printk("#####################################################\n");
printk("#####################################################\n");
printk("#####################################################\n");
printk("#####################################################\n");
printk("myhello is running\n");
printk("#####################################################\n");
printk("#####################################################\n");
printk("#####################################################\n");
printk("#####################################################\n");
return 0;
}
void __exit myhello_exit(void)
{
printk("myhello will exit\n");
}
MODULE_LICENSE("GPL");
module_init(myhello_init);
module_exit(myhello_exit);
模块三要素:入口函数、出口函数、MODULE__LICENSE
三、内核模块的多源文件编程
ifeq ($(KERNELRELEASE),)
ifeq ($(ARCH),arm)
KERNELDIR ?= 目标板linux内核源码顶层目录的绝对路径
ROOTFS ?= 目标板根文件系统顶层目录的绝对路径
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
endif
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) INSTALL_MOD_PATH=$(ROOTFS) modules_install
clean:
rm -rf *.o *.ko .*.cmd *.mod.* modules.order Module.symvers .tmp_versions
else
obj-m += hello.o
endif
Makefile中:
obj-m 用来指定模块名,注意模块名加.o 而不是.ko
可以用 模块名-objs 变量 来指定编译到ko中 的所有.o文件名 (每个同名的.c文件对应的.o目标文件)
一个目录下的Makefile可以编译多个模块:
添加:obj-m += 下一个模块名.o
四、 内核模块信息宏
MODULE_AUTHOR(字符串常量);
MODULE_DESCRIPTION(字符串常量);
MODULE_ALIAS(字符串常量);
这些宏用来描述一些当前模块的信息,可选宏
这些宏的本质是定义static字符数组用于存放指定字符串内容,这些字符串内容链接时存放在.modinfo字段,可以用modinfo命令来查看这些模块信息,用法:
modinfo 模块文件名
到这里就结束啦!
|