linux kernel module

一、什么是内核模块

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

注意:驱动是安装到内存中正在运行的内核上的。

二、设计一个最简单的module

1.参考内核源码

例子:/drivers/watchdog/mxp_wdt.c

2.设计Led driver module

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

//入口函数--->安装驱动
static int __init S5P6818_led_init(void)
{
	printk(KERN_WARNING "S5P6818 led driver init \n");
	
	return 0;
}

//出口函数--->卸载驱动
static void __exit S5P6818_led_exit(void)
{
	printk(KERN_WARNING "S5P6818 led driver exit \n");
}

//驱动程序的入口:#insmod led_drv.ko -->module_init()-->S5P6818_led_init()
module_init(S5P6818_led_init);
//驱动程序的出口:#rmmod led_drv.ko --->module_exit()-->S5P6818_led_exit()
module_exit(S5P6818_led_exit);

//module的描述。#modinfo led_drv.ko
MODULE_AUTHOR("wslinkm@163.com");
MODULE_DESCRIPTION("LED driver for S5P6818");
MODULE_LICENSE("GPL");
MODULE_VERSION("V1.0");

三、Makefile

obj-m += led_drv.o
KERNELDIR:=/home/wsl/kernel/S5P6818/kernel
CROSS_COMPILE:=/home/wsl/kernel/S5P6818/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

clean:
	rm -rf *.o *.order .*.cmd *.ko *.mod.c *.symvers *.tmp_versions

说明:

1. obj-m += led_drv.o

将源程序的目标文件led_drv.o,编译成一个module(ko)

注意:+=,?=, := 区别?
= 是最基本的赋值
:= 是覆盖之前的值
?= 是如果没有被赋值过就赋予等号后面的值
+= 是添加等号后面的值

“=” 表示make会将整个makefile展开后,再决定变量的值。也就是说,变量的值将会是整个makefile中最后被指定的值。例子:
x = foo
y = $(x) bar
x = end

在上例中,y的值将会是 end bar ,而不是 foo bar 。

“:=” 表示变量的值决定于它在makefile中的位置,而不是整个makefile展开后的最终值。
x := foo
y := $(x) bar
x := end

在上例中,y的值将会是 foo bar ,而不是 end bar 了。

2. KERNELDIR:=/home/wsl/kernel/S5P6818/kernel

内核源码的路径:内核的头文件和Makefile

3. CROSS_COMPILE:=/home/wsl/kernel/S5P6818/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-

交叉编译工具

4. PWD:=$(shell pwd)

当前路径

5. ( M A K E ) A R C H = a r m C R O S S C O M P I L E = (MAKE) ARCH=arm CROSS_COMPILE= (MAKE)ARCH=armCROSSCOMPILE=(CROSS_COMPILE) -C ( K E R N E L D I R ) M = (KERNELDIR) M= (KERNELDIR)M=(PWD) modules

向内核源码路径下的Makefile文件传递两个参数,并调用内核源码下的Makefile文件,使用该Makfile中的工具,回到当前路径下,将源程序编译成一个module。

四、驱动的调试

1. file

root@wsl-VirtualBox:/home/VMShareDir/CodeLinux/Linux_kernel/004module# file led_drv.ko 
led_drv.ko: ELF 32-bit LSB relocatable, ARM, EABI5 version 1 (SYSV), BuildID[sha1]=50316234980f9f06bc0c138bf8f43e42dced2f28, with debug_info, not stripped

2. size

root@wsl-VirtualBox:/home/VMShareDir/CodeLinux/Linux_kernel/004module# size led_drv.ko
   text	   data	    bss	    dec	    hex	filename
    360	    360	      0	    720	    2d0	led_drv.ko

3. modinfo

root@wsl-VirtualBox:/home/VMShareDir/CodeLinux/Linux_kernel/004module# modinfo led_drv.ko
filename:       /home/VMShareDir/CodeLinux/Linux_kernel/004module/led_drv.ko
version:        V1.0
license:        GPL
description:    LED driver for S5P6818
author:         wslinkm@163.com
srcversion:     753552DDFB9C4FE54972420
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 #37 SMP PREEMPT Tue Aug 1 13:53:02 CST 2017 armv7l GNU/Linux

驱动程序(ko)的版本与目标平台的版本要一致。

4. 驱动安装

[root@GEC6818 /tmp]#insmod led_drv.ko
[ 1870.798000] S5P6818 led driver init 

5. 查看module

[root@GEC6818 /tmp]#lsmod
led_drv 768 0 - Live 0xbf039000 (O)

6. 卸载驱动

[root@GEC6818 /tmp]#rmmod led_drv.ko 
[ 2038.228000] S5P6818 led driver exit 

五、编译驱动时,对内核源码的要求

1、内核源码的版本要和驱动安装目标平台的版本一致
2、内核源码要针对目标平台的CPU架构配置过
3、内核源码必须要编译过。

六、printk

应用程序:stdio.h ---->printf()
驱动程序:kernel.h ---->printk()

printk()带有优先级的。

1、查看系统printk的优先级

#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)简单方法
开发板平台

#cat /proc/sys/kernel/printk
7       7       1       7

#echo 7 4 1 7 >/proc/sys/kernel/printk


永久生效,写入:/etc/profile
#echo 7 4 1 7 >/proc/sys/kernel/printk

2)printk加优先级

printk(KERN_WARNING "gec6818 led driver init \n");
printk("<4>" "gec6818 led driver exit \n");

3)配置linux内核,修改优先级—>一劳永逸
虚拟机平台
(1)使用默认的配置文件

root@wsl-VirtualBox:~/kernel/S5P6818/kernel# cp arch/arm/configs/GEC6818_defconfig  .config

(2)make menuconfig–>配置内核

root@wsl-VirtualBox:~/kernel/S5P6818/kernel# make menuconfig

============================================================
报错:
root@wsl-VirtualBox:~/kernel/S5P6818/kernel# make menuconfig
scripts/kconfig/mconf Kconfig
Your display is too small to run Menuconfig!
It must be at least 19 lines by 80 columns.
/home/wsl/kernel/S5P6818/kernel/scripts/kconfig/Makefile:21: recipe for target ‘menuconfig’ failed
make[1]: *** [menuconfig] Error 1
Makefile:492: recipe for target ‘menuconfig’ failed
make: *** [menuconfig] Error 2

原因:terminal 窗口太小的原因
解决:把terminal变大整个屏幕大小就可以显示

============================================================
Kernel hacking —> (4) Default message log level (1-7)

(3)保存退出
(4)复制配置文件

root@wsl-VirtualBox:~/kernel/S5P6818/kernel#cp .config  arch/arm/configs/GEC6818_defconfig

(5)编译内核

./mk -k

---->烧写内核镜像 out/release/boot.img

七、内核符号表

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## 3、 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 ---->释放了内存。

九、驱动程序和应用程序的区别

1、驱动程序有入口和出口,但是应用程序只有入口–main()
2、设计驱动程序的时候,只能使用内核源码提供的头文件–>/include
不能使用标准的C库:stdio.h,printf()
3、驱动程序是一个个独立的模块。各个驱动程序之间,一般是没有关系的。

4、编译方法
应用程序:arm-linux-gcc -o test test.c
驱动程序:
使用内核源码包提供的头文件、使用内核源码的编译工具:Makefile

5、驱动程序要求稳定、高效、精简。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值