企业网站建设中有哪几个重要点,免费自动建站,深入解析wordpress(原书第2版) pdf,网站建设论文基础摘要文章目录 前言一、静态加载法1.1 编写驱动程序1.2 将新功能配置在内核中1.3为新功能代码改写Makefile1.4 make menuconfig界面里将新功能对应的那项选择为* 二、动态加载法2.1 新功能源码与Linux内核源码在同一目录结构下2.2 新功能源码与Linux内核源码不在同一目录结构… 文章目录 前言一、静态加载法1.1 编写驱动程序1.2 将新功能配置在内核中1.3为新功能代码改写Makefile1.4 make menuconfig界面里将新功能对应的那项选择为* 二、动态加载法2.1 新功能源码与Linux内核源码在同一目录结构下2.2 新功能源码与Linux内核源码不在同一目录结构下2.3 主机ubuntu下使用ko文件2.4 开发板Linux下使用ko文件 三、内核模块基础代码解析四、 内核模块的多源文件编程五、 内核模块信息宏总结 前言
本期和大家主要分享的是关于驱动开发中内核模块的编译方法向内核中增加新功能主要分为两种方法静态加载法和动态加载法两种方法分别有它们各自的优缺点接下来一起看看吧 提示以下是本篇文章正文内容下面案例可供参考
一、静态加载法
静态加载法新功能源码与内核源码再同一目录结构下
1.1 编写驱动程序
在linux-3.14/driver/char/目录下编写myhello.c文件内容如下
#include linux/module.h
#include linux/kernel.hint __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);1.2 将新功能配置在内核中
1进入myhello.c的同级目录 cd ~/fa4412/linux-3.14/drivers/char vim Kconfig 2在39行处添加如下内容 config MY_HELLO tristate “This is a hello test” help This is a test for kernel new function 其本质是为了在执行make menuconfig产生的图形化界面中添加This is a hello test这一项这一项被选中即表示MY_HELLO添加到内核中
1.3为新功能代码改写Makefile 进入myhello.c的同级目录 cd ~/fs4412/linux-3.14/drivers/char vim Makefile obj-$(CONFIG_MY_HELLO) myhello.ko 注意这里的CONFIG_MY_HELLO中的MY_HELLO应该与1.2中配置Kconfig的名字一致本质是为了使得新添加的驱动程序myhello.c加入内核编译 1.4 make menuconfig界面里将新功能对应的那项选择为*
这里顺便提一下 Y–将该功能编译进内核 N–不将该功能编译进内核 M–将该功能编译成可以在需要时动态插入到内核中的模块 cd ~/fs4412/linux-3.14 make menuconfig make menuconfig如果出错一般是两个原因 libncurses5-dev没安装命令行界面太小太矮或太窄或字体太大了 上述操作结束后接下来回到linux源码顶层目录进行make uImage操作紧接着将新生成的内核镜像拷贝到tftp目录下cp arch/arm/boot/uImage /tftpboot,最终将uImage下载到开发板会在启动内核的打印信息中观察到 这表示自己添加的驱动程序已经添加到了内核中这种方法称为静态加载法
二、动态加载法
动态加载发新功能和内核其他源码不一起编译而是独立编译成内核的插件内核模块文件格式为文件.ko
2.1 新功能源码与Linux内核源码在同一目录结构下 给新功能代码配置Kconfig 给新功能代码改写Makefile make menuconfig 界面里将新功能对应的那项选择成 make uImage cp arch/arm/boot/uImage /tftpboot make modules make modules会在新功能源码的同级目录下生成相应的同名.ko文件生成的ko文件只适用于开发板linux 注意此命令执行前开发板的内核源码已被编译
2.2 新功能源码与Linux内核源码不在同一目录结构下
cd ~/fs4412mkdir mydrivercodecd mydrivercodecp …/linux-3.14/drivers/char/myhello.c .vim Makefilemake 生成的ko文件适用于主机ubuntu linuxmake ARCHarm 生成的ko文件适用于开发板linux注意此命令执行前开发板的内核源码已被编译
file命令可以查看指定ko文件适用于哪种平台用法 file ko文件 #结果带x86字样的适用于主机ubuntu linux带arm字样的适用于开发板linux
2.3 主机ubuntu下使用ko文件 sudo insmod ./???.ko #此处为内核模块文件名将内核模块插入正在执行的内核中运行 ----- 相当于安装插件 lsmod #查看已被插入的内核模块有哪些显示的是插入内核后的模块名 sudo rmmod ??? #此处为插入内核后的模块名此时将已被插入的内核模块从内核中移除 ----- 相当于卸载插件 sudo dmesg -C #清除内核已打印的信息 dmesg #查看内核的打印信息 2.4 开发板Linux下使用ko文件
#先将生成的ko文件拷贝到/opt/4412/rootfs目录下 cp ???/???.ko /opt/4412/rootfs #在串口终端界面开发板Linux命令行下执行 insmod ./???.ko #将内核模块插入正在执行的内核中运行 ----- 相当于安装插件 lsmod #查看已被插入的内核模块有哪些 rmmod ??? #将已被插入的内核模块从内核中移除 ----- 相当于卸载插件 内核随时打印信息我们可以在串口终端界面随时看到打印信息不需要dmesg命令查看打印信息
三、内核模块基础代码解析
Linux内核的插件机制——内核模块
类似于浏览器、eclipse这些软件的插件开发Linux提供了一种可以向正在运行的内核中插入新的代码段、在代码段不需要继续运行时也可以从内核中移除的机制这个可以被插入、移除的代码段被称为内核模块。
主要解决
单内核扩展性差的缺点减小内核镜像文件体积一定程度上节省内存资源提高开发效率不能彻底解决稳定性低的缺点内核模块代码出错可能会导致整个系统崩溃
内核模块的本质一段隶属于内核的“动态”代码与其它内核代码是同一个运行实体共用同一套运行资源只是存在形式上是独立的。
#include linux/module.h //包含内核编程最常用的函数声明如printk
#include linux/kernel.h //包含模块编程相关的宏定义如MODULE_LICENSE/*该函数在模块被插入进内核时调用主要作用为新功能做好预备工作被称为模块的入口函数__init的作用 :
1. 一个宏展开后为__attribute__ ((__section__ (.init.text))) 实际是gcc的一个特殊链接标记
2. 指示链接器将该函数放置在 .init.text区段
3. 在模块插入时方便内核从ko文件指定位置读取入口函数的指令到特定内存位置
*/
int __init myhello_init(void)
{/*内核是裸机程序不可以调用C库中printf函数来打印程序信息Linux内核源码自身实现了一个用法与printf差不多的函数命名为printk k-kernelprintk不支持浮点数打印*/printk(#####################################################\n);printk(#####################################################\n);printk(#####################################################\n);printk(#####################################################\n);printk(myhello is running\n);printk(#####################################################\n);printk(#####################################################\n);printk(#####################################################\n);printk(#####################################################\n);return 0;
}/*该函数在模块从内核中被移除时调用主要作用做些init函数的反操作被称为模块的出口函数__exit的作用
1.一个宏展开后为__attribute__ ((__section__ (.exit.text))) 实际也是gcc的一个特殊链接标记
2.指示链接器将该函数放置在 .exit.text区段
3.在模块插入时方便内核从ko文件指定位置读取出口函数的指令到另一个特定内存位置
*/
void __exit myhello_exit(void)
{printk(myhello will exit\n);
}/*
MODULE_LICENSE(字符串常量);
字符串常量内容为源码的许可证协议 可以是GPL GPL v2 GPL and additional rights Dual BSD/GPL Dual MIT/GPL Dual MPL/GPL等, GPL最常用其本质也是一个宏宏体也是一个特殊链接标记指示链接器在ko文件指定位置说明本模块源码遵循的许可证
在模块插入到内核时内核会检查新模块的许可证是不是也遵循GPL协议如果发现不遵循GPL则在插入模块时打印抱怨信息myhellomodule license unspecified taints kernelDisabling lock debugging due to kernel taint
也会导致新模块没法使用一些内核其它模块提供的高级功能
*/
MODULE_LICENSE(GPL);/*
module_init 宏
1. 用法module_init(模块入口函数名)
2. 动态加载模块对应函数被调用
3. 静态加载模块内核启动过程中对应函数被调用
4. 对于静态加载的模块其本质是定义一个全局函数指针并将其赋值为指定函数链接时将地址放到特殊区段.initcall段方便系统初始化统一调用。
5. 对于动态加载的模块由于内核模块的默认入口函数名是init_module,用该宏可以给对应模块入口函数起别名
*/
module_init(myhello_init);/*
module_exit宏
1.用法module_exit(模块出口函数名)
2.动态加载的模块在卸载时对应函数被调用
3.静态加载的模块可以认为在系统退出时对应函数被调用实际上对应函数被忽略
4.对于静态加载的模块其本质是定义一个全局函数指针并将其赋值为指定函数链接时将地址放到特殊区段.exitcall段方便系统必要时统一调用实际上该宏在静态加载时没有意义因为静态编译的驱动无法卸载。
5.对于动态加载的模块由于内核模块的默认出口函数名是cleanup_module,用该宏可以给对应模块出口函数起别名
*/
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) modulesmodules_install:$(MAKE) -C $(KERNELDIR) M$(PWD) INSTALL_MOD_PATH$(ROOTFS) modules_installclean:rm -rf *.o *.ko .*.cmd *.mod.* modules.order Module.symvers .tmp_versionselse
obj-m hello.oendifMakefile中
obj-m用来指定模块名注意模块名加.o而不是.ko
可以用 模块名-objs 变量来指定编译到ko中的所有.o文件名每个同名的.c文件对应的.o目标文件
一个目录下的Makefile可以编译多个模块
添加obj-m 下一个模块名.o
五、 内核模块信息宏
MODULE_AUTHOR(字符串常量); //字符串常量内容为模块作者说明MODULE_DESCRIPTION(字符串常量); //字符串常量内容为模块功能说明MODULE_ALIAS(字符串常量); //字符串常量内容为模块别名这些宏用来描述一些当前模块的信息可选宏
这些宏的本质是定义static字符数组用于存放指定字符串内容这些字符串内容链接时存放在.modinfo字段可以用modinfo命令来查看这些模块信息用法
modinfo 模块文件名总结
本期主要分享的是两种非常重要的内核加载方法每种方法都有各自的优缺点在开发中经常使用动态加载法来提高开发效率不仅给出了两种加载新内核功能的方法并且给出了内核驱动的编写基本框架希望小伙伴们认真掌握理解哦 最后各位小伙伴们如果喜欢我的分享可以点赞收藏哦你们的认可是我创作的动力一起加油