目录
一、什么是内核模块
1.linux kernel module
驱动程序在内核中,都是独立的模块,例如:beep驱动和LED驱动,beep和led之间没有任何联系,可以通过应用程序将两个驱动联系在一起。beep驱动和led驱动各自是独立的module。
说明:每个驱动程序都是一个独立的模块,每设计一个驱动程序,首先设计一个module,驱动程序是包含在module中的。
2、module编译后会生成一个.ko*
驱动程序:可以安装、可以卸载的
安装驱动:#insmod led_drv.ko
卸载驱动:#rmmod led_drv.ko
查看系统中,安装的module:#lsmod
注意:驱动是安装到内存中正在运行的内核上的。
二、使用source insght创建一个工程
1、SI设置
Options—>Document Options—>Document Type:C Source File :.c;.h;.S;.s
X86 Asm Source File: .asm;.inc;.S;.s
2、创建一个工程
project–>new project–>工程文件放在源码包中
add tree —> close
3、文件的同步
project —> synchronize Files
三、设计一个最简单的module
注意:参考内核源码,首先使用source insight创建一个内核源码的工程
例子:
/drivers/watchdog/mxp_wdt.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
//入口函数--->安装驱动
static int __init gec6818_led_init(void)
{
printk("gec6818 led driver init \n");
return 0;
}
//出口函数--->卸载驱动
static void __exit gec6818_led_exit(void)
{
printk("gec6818 led driver exit \n");
}
//驱动程序的入口:#insmod led_drv.ko -->module_init()–>gec6818_led_init()
module_init(gec6818_led_init);
//驱动程序的出口:#rmmod led_drv.ko —>module_exit()–>gec6818_led_exit()
module_exit(gec6818_led_exit);
//module的描述。#modinfo led_drv.ko
MODULE_AUTHOR(“bobeyfeng@163.com”);
MODULE_DESCRIPTION(“LED driver for GEC6818”);
MODULE_LICENSE(“GPL”);
MODULE_VERSION(“V1.0”);
四、驱动程序和应用程序的区别
1、驱动程序有入口和出口,但是应用程序只有入口–main()
2、设计驱动程序的时候,只能使用内核源码提供的头文件–>/include
不能使用标准的C库:stdio.h,printf()
3、驱动程序是一个个独立的模块。各个驱动程序之间,一般是没有关系的。
4、编译方法
应用程序:arm-linux-gcc -o test test.c
驱动程序:
使用内核源码包提供的头文件、使用内核源码的编译工具:Makefile
5、驱动程序要求稳定、高效、精简。
五、Makefile
1、编译输出:
$ make
make ARCH=arm CROSS_COMPILE=/home/bobey/6818GEC/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi- -C /home/bobey/6818GEC/kernel M=/mnt/hgfs/linux内核驱动/4module/demo modules
make[1]: Entering directory '/home/bobey/6818GEC/kernel'
CC [M] /mnt/hgfs/linux内核驱动/4module/demo/led_drv.o
Building modules, stage 2.
MODPOST 1 modules
CC /mnt/hgfs/linux内核驱动/4module/demo/led_drv.mod.o
LD [M] /mnt/hgfs/linux内核驱动/4module/demo/led_drv.ko
make[1]: Leaving directory '/home/bobey/6818GEC/kernel'
2、Makefile
obj-m += led_drv.o
KERNELDIR:=/home/bobey/6818GEC/kernel
CROSS_COMPILE:=/home/bobey/6818GEC/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-
PWD:=$(shell pwd)
default:
$(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) -C \
$(KERNELDIR) M=$(PWD) modules
说明:
1)obj-m += led_drv.o
注意:+=,?=, := 区别?
将源程序的目标文件led_drv.o,编译成一个module(ko)
2)KERNELDIR:=/home/bobey/6818GEC/kernel
内核源码的路径:内核的头文件和Makefile
3)CROSS_COMPILE:=/home/bobey/6818GEC/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-
交叉编译工具
4)PWD:=$(shell pwd)
当前路径
5)$(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNELDIR) M=$(PWD) modules
向内核源码路径下的Makefile文件传递两个参数,并调用内核源码下的Makefile文件,使用该Makfile中的工具,回到当前路径下,将源程序编译成一个module。
六、驱动的调试
1、file
$ file led_drv.ko
led_drv.ko: ELF 32-bit LSB relocatable, ARM, EABI5 version 1 (SYSV), BuildID[sha1]=04ff90444af056f8c1c04f119c7307950bfe16a5, not stripped
2、size
$ size led_drv.ko
text data bss dec hex filename
344 360 0 704 2c0 led_drv.ko
3、modinfo
$ modinfo led_drv.ko
filename: /mnt/hgfs/linux内核驱动/4module/demo/led_drv.ko
version: V1.0
license: GPL
description: LED driver for GEC6818
author: bobeyfeng@163.com
srcversion: 5D5F2D6C66A08F289709359
depends:
vermagic: 3.4.39-gec SMP preempt mod_unload ARMv7 p2v8
vermagic —>version magic(魔数):驱动可以安装的linux版本:3.4.39-gec,其中:-gec —>local version,配置内核的时候
ARMv7 ---->硬件的版本
GEC6818平台:
[root@GEC6818 /]#uname -a
Linux GEC6818 3.4.39-gec #4 SMP PREEMPT Tue Oct 24 21:09:31 CST 2017 armv7l GNU/Linux
驱动程序(ko)的版本与目标平台的版本要一致。
4、驱动安装
[root@GEC6818 /]#insmod led_drv.ko
[ 292.145000] gec6818 led driver init
5、查看module
[root@GEC6818 /]#lsmod
led_drv 760 0 - Live 0xbf000000 (O)
6、卸载驱动
[root@GEC6818 /]#rmmod led_drv.ko
[ 364.399000] gec6818 led driver exit
七、编译驱动时,对内核源码的要求
1、内核源码的版本要和驱动安装目标平台的版本一致
2、内核源码要针对目标平台的CPU架构配置过
3、内核源码必须要编译过。
八、printk
应用程序:stdio.h ---->printf()
驱动程序:kernel.h ---->printk()
printk()带有优先级的。
1、查看系统printk的优先级
[root@GEC6818 /]#cat /proc/sys/kernel/printk
7 7 1 7
该文件有四个数字值,它们根据日志记录消息的重要性,定义将其发送到何处。关于不同日志级别的更多信息,请查阅syslog(2)联机帮助。上面显示的4个数据分别对应:
7—>控制台日志级别:优先级高于该值的消息将被打印至控制台
7—>默认的消息日志级别:将用该优先级来打印没有优先级的消息,printk(“gec6818 led driver init \n”);
1—>最低的控制台日志级别:控制台日志级别可被设置的最小值(最高优先级)
7—>默认的控制台日志级别:控制台日志级别的缺省值
数值越小,优先级越高
2、printk优先级的含义
#define KERN_EMERG "<0>" /* system is unusable */
#define KERN_ALERT "<1>" /* action must be taken immediately */
#define KERN_CRIT "<2>" /* critical conditions */
#define KERN_ERR "<3>" /* error conditions */
#define KERN_WARNING "<4>" /* warning conditions */
#define KERN_NOTICE "<5>" /* normal but significant condition */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */
3、如何解决printk输出看不看内容的问题?
----->设置printk的优先级
1)简单方法
[root@GEC6818 /]#cat /proc/sys/kernel/printk
7 7 1 7
[root@GEC6818 /]#echo 7 4 1 7 >/proc/sys/kernel/printk
将
echo 7 4 1 7 >/proc/sys/kernel/printk
写入:/etc/profile
2)printk加优先级
printk(KERN_WARNING “gec6818 led driver init \n”);
printk("<4>" “gec6818 led driver exit \n”);
3)配置linux内核,修改优先级—>一劳永逸
(1)使用默认的配置文件
bobey@ubuntu:~/6818GEC/kernel$ cp arch/arm/configs/GEC6818_defconfig .config
(2)make menuconfig–>配置内核
sudo apt-get update
sudo apt-get install libncurses5-dev
#make menuconfig
Kernel hacking —>
(3)Default message log level (1-7)
(4)保存退出
(5)复制配置文件
cp .config arch/arm/configs/GEC6818_defconfig
(6)编译内核
./mk -k
/home/bobey/6818GEC/out/release/boot.img —>烧写到emmc
九、内核符号表
1、什么是内核符号表
内核符号表是内核中一个全局的总编,这个表中,申明了一些全局的函数,内核中的驱动程序就可以直接调用这些函数。
[root@GEC6818 /]#cat /proc/kallsyms
2、如何将一个函数名,申明到内核符号表中去
struct net_device_stats *eip_get_stats(struct net_device *dev)
{
return __ei_get_stats(dev);
}
EXPORT_SYMBOL(eip_get_stats);
EXPORT_SYMBOL()
EXPORT_SYMBOL_GPL() —>声明内核符号表中的函数,只有符合GPL协议的驱动才可以使用
如何让一个驱动符合GPL协议???
MODULE_LICENSE(“GPL”);
十、__init 和 __exit
__init用来修饰一个内核中一个初始化函数,初始化函数的特点是在系统初始化的时候,执行一次这样的函数,后面就不用了。
这个时候初始化函数所占用的内存可以释放掉。
内核的启动过程:
[ 0.000000] Memory: 1024MB = 1024MB total
[ 0.000000] Memory: 810820k/810820k available, 237756k reserved, 272384K highmem
[ 0.000000] Virtual kernel memory layout:
[ 0.000000] vector : 0xffff0000 - 0xffff1000 ( 4 kB)
[ 0.000000] fixmap : 0xfff00000 - 0xfffe0000 ( 896 kB)
[ 0.000000] vmalloc : 0xef800000 - 0xfee00000 ( 246 MB)
[ 0.000000] lowmem : 0xc0000000 - 0xef600000 ( 758 MB)
[ 0.000000] pkmap : 0xbfe00000 - 0xc0000000 ( 2 MB)
[ 0.000000] modules : 0xbf000000 - 0xbfe00000 ( 14 MB)
[ 0.000000] .text : 0xc0008000 - 0xc0a51018 (10533 kB)
[ 0.000000] .init : 0xc0a52000 - 0xc0a8f100 ( 245 kB) ---->内核初始化函数所占用的内存
[ 0.000000] .data : 0xc0a90000 - 0xc0b297d8 ( 614 kB)
[ 0.000000] .bss : 0xc0b297fc - 0xc0d09488 (1920 kB)
....................................
[ 3.927000] EXT4-fs (mmcblk0p2): recovery complete
[ 3.927000] EXT4-fs (mmcblk0p2): mounted filesystem with ordered data mode. Opts: (null)
[ 3.934000] VFS: Mounted root (ext4 filesystem) on device 179:2.
[ 3.940000] devtmpfs: mounted
[ 3.943000] Freeing init memory: 244K ---->释放了内存。