(二)内核模块

一、什么是内核模块

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  ---->释放了内存。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值