内核模块--module学习

一、内核模块

1.什么是内核模块 .ko
驱动程序已编译通过、未经连接的目标文件
.ko,不能独立运行
需要使用时,动态加载 insmod *.ko
不需要时卸载 rmmod
优点:
(1)减小内核镜像的尺寸,灵活加载卸载
(2)不需要重新编译内核,缩短项目开发时间
缺点:
(1)降低系统运行效率
(2)模块本身有缺陷的话,使用不当会导致内核崩溃

2.如何设计一个内核模块程序
linux内核模块程序没有main函数,至少有两个函数:入口(初始化)函数、出口(卸载)函数
加载模块时,入口函数被内核调用
卸载模块时,出口函数被内核调用

static int  __init xxx_init(void)
{
	//由驱动程序员决定
	return 0;----函数正常执行结束
}
static void __exit xxx_exit(void)
{
 //由驱动程序员决定
 }

module_init(xxx_init);—告诉内核xxx_init是入口函数
module_exit(xxx_exit);—告诉内核xxx_exit是出口函数
MODULE_LICENSE(“GPL”);—声明该模块程序遵循GPL协议

3.内核模块程序的编译
条件:(1)需要内核源码
(2)内核源码针对硬件平台配置过
(3)内核源码正确地编译通过

编写Makefile

obj-m += module.o #obj-m编译成内核模块*.ko 思考:+= := ?= = 有何区别
CROSS_DIR=/home/gec/6818GEC/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-
KERNEL_DIR=/home/gec/6818GEC/kernel
all:
 #用编译内核源码的编译规则来编译当前目录下的内核模块程序,编译成*.ko
 make ARCH=arm CROSS_COMPILE=$(CROSS_DIR)  -C $(KERNEL_DIR) M=`pwd` modules
clean:
 make ARCH=arm CROSS_COMPILE=$(CROSS_DIR)  -C $(KERNEL_DIR) M=`pwd` modules clean

4.传输*.ko到开发板的根文件系统
(1)串口方式
[root@GEC6818 /]#rx module.ko------->点击“传输”---->点击发送Xmodem—>选中*.ko
CCCCC
xmodem trl+C ?

(2)tftp
[root@GEC6818 /]#tftp -g -r module.ko 192.168.11.3

(3)nfs
[root@GEC6818 /]#mount -t nfs -o nolock 192.168.11.3:/opt/nfs /mnt
[root@GEC6818 /]#cd /mnt
[root@GEC6818 /mnt]#ls
[root@GEC6818 /mnt]#ls
module.ko

加载模块时,入口函数被内核调用
insmod module.ko

问题1:
[root@GEC6818 /mnt]#insmod module.ko
[ 4452.871000] module: version magic '3.4.39-xxx SMP preempt mod_unload ARMv7 p2v8 ’ should be '3.4.39-1862 SMP preempt mod_unload ARMv7 p2v8 ’
insmod: can’t insert ‘module.ko’: invalid module format
[root@GEC6818 /mnt]#uname -r
3.4.39-1862
编译内核模块使用的内核版本是3.4.39-xxx ,开发板上运行的内核的版本是3.4.39-1862 ,不一致导致加载失败

解决:
方法(1)把版本为3.4.39-xxx 的内核镜像刷机
方法(2)把编译内核模块使用的内核源码版本改为3.4.39-1862
a.进入内核源码目录
b. make menuconfig

   General setup  ---> 
 (-1862) Local version - append to kernel release     

c.保存退出
d.gec@ubuntu:~/6818GEC/kernel$ cp .config arch/arm/configs/GEC6818_defconfig
gec@ubuntu:~/6818GEC/kernel$ cd …
gec@ubuntu:~/6818GEC$ ./mk -k
e.重新编译内核模块


内核模块相关的几个命令

modinfo-----查看内核模块文件的相关信息
gec@ubuntu:/mnt/hgfs/driver/3.module/code/module1$ modinfo module.ko
filename: /mnt/hgfs/driver/3.module/code/module1/module.ko
description: just a test module
author: XLG
license: GPL
depends:
vermagic: 3.4.39-1862 SMP preempt mod_unload ARMv7 p2v8

lsmod-----罗列出系统中动态加载的内核模块
[root@GEC6818 /mnt]#lsmod
module 720 0 - Live 0xbf000000 (O)


rmmod -----------卸载内核模块
[root@GEC6818 /mnt]#rmmod module
rmmod: can’t change directory to ‘3.4.39-1862’: No such file or directory

问题2:卸载模块时,提示3.4.39-1862目录文件没有
解决:
[root@GEC6818 /lib/modules]#mkdir 3.4.39-1862

问题3:
加载卸载内核模块时,printk并没有打出信息
原因:printk输出的信息是有优先级的,低级别的信息没有打印出来

printk.h (include\linux) 9233 2016/7/18
#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 */

[root@GEC6818 /lib/modules]#cat /proc/sys/kernel/printk
7 7 1 7

int console_printk[4] = {
DEFAULT_CONSOLE_LOGLEVEL, /* console_loglevel /
DEFAULT_MESSAGE_LOGLEVEL, /
default_message_loglevel /
MINIMUM_CONSOLE_LOGLEVEL, /
minimum_console_loglevel /
DEFAULT_CONSOLE_LOGLEVEL, /
default_console_loglevel */
};

7------只有级别比该级别高的信息才会打印显示
7------默认的消息日志信息级别,printk(“xxx_init\n”);没有指定级别时,默认的级别
1-----控制台日志信息所能设置的最高级别
7-----默认的控制台日志级别

解决办法:
方法(1):printk("<4>"“xxx_init\n”) 或者printk(KERN_ALERT"xxx_init\n")
方法(2):[root@GEC6818 /mnt]#echo “7 4 1 7” > /proc/sys/kernel/printk
临时有效,系统重启,变回"7 7 1 7"
方法(3):重新配置编译内核,刷机
a.进入内核源码目录
b. make menuconfig
Kernel hacking —>
(4) Default message log level (1-7)
c.保存退出
d.gec@ubuntu:~/6818GEC/kernel$ cp .config arch/arm/configs/GEC6818_defconfig
gec@ubuntu:~/6818GEC/kernel$ cd …
gec@ubuntu:~/6818GEC$ ./mk -k
e.得到新的内核镜像后,刷机

二、内核模块使用的一些技巧

1.一个驱动模块调用了另一个驱动模块的某个函数
修改Makefile
obj-m += module.o #obj-m编译成内核模块*.ko 思考:+= := ?= = 有何区别
obj-m += add.o

在add.c中
int xxx_add(int x,int y)
{
return (x+y);
}

EXPORT_SYMBOL(xxx_add);----把xxx_add导入到内核的全局函数符号表
[root@GEC6818 /mnt]#cat /proc/kallsyms | grep “xxx_add”
bf024064 r __kstrtab_xxx_add [add]
bf024038 r __ksymtab_xxx_add [add]
bf024000 T xxx_add [add]

注意:
加载时,先加载add.ko 后加载module.ko
卸载时,先卸载module.ko ,后卸载add.ko

2.如何把多个*.c,编译成一个*.ko
修改Makefile
obj-m += xxx.o
xxx-objs += add.o module.o
add.c中只保留函数xxx_add的定义

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只很笨很懒的肥猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值