LV06-09-调试与优化-01-代码优化
驱动中一些地方还可以做一些优化来提高稳定性和效率,怎么优化?若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
PC端开发环境 | Windows | Windows11 |
Ubuntu | Ubuntu20.04.2的64位版本 | |
VMware® Workstation 17 Pro | 17.6.0 build-24238078 | |
终端软件 | MobaXterm(Professional Edition v23.0 Build 5042 (license)) | |
Win32DiskImager | Win32DiskImager v1.0 | |
Linux开发板环境 | Linux开发板 | 正点原子 i.MX6ULL Linux 阿尔法开发板 |
uboot | NXP官方提供的uboot,使用的uboot版本为U-Boot 2019.04 | |
linux内核 | linux-4.19.71(NXP官方提供) |
点击查看本文参考资料
分类 | 网址 | 说明 |
官方网站 | https://www.arm.com/ | ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档 |
https://www.nxp.com.cn/ | NXP官方网站 | |
https://www.nxpic.org.cn/ | NXP 官方社区 | |
https://u-boot.readthedocs.io/en/latest/ | u-boot官网 | |
https://www.kernel.org/ | linux内核官网 |
点击查看相关文件下载
分类 | 网址 | 说明 |
NXP | https://github.com/nxp-imx | NXP imx开发资源GitHub组织,里边会有u-boot和linux内核的仓库 |
nxp-imx/linux-imx/releases/tag/v4.19.71 | NXP linux内核仓库tags中的v4.19.71 | |
nxp-imx/uboot-imx/releases/tag/rel_imx_4.19.35_1.1.0 | NXP u-boot仓库tags中的rel_imx_4.19.35_1.1.0 | |
I.MX6ULL | i.MX 6ULL Applications Processors for Industrial Products | I.MX6ULL 芯片手册(datasheet,可以在线查看) |
i.MX 6ULL Applications ProcessorReference Manual | I.MX6ULL 参考手册(下载后才能查看,需要登录NXP官网) | |
Source Code | https://elixir.bootlin.com/linux/latest/source | linux kernel源码 |
kernel/git/stable/linux.git - Linux kernel stable tree | linux kernel源码(官网,tag 4.19.71) | |
https://elixir.bootlin.com/u-boot/latest/source | uboot源码 |
一、用户空间内存块检测
但是其实这个不检查也问题不大,因为我们一般在用户空间肯定会做检查,检查完毕才会继续往下执行。
1. access_ok()函数
这个函数的作用是检查用户空间内存块是否可用,它可能的定义如下:
1 | access_ok(type, addr, size); |
参数说明:
type :Type of access,VERIFY_READ or VERIFY_WRITE。请注意,VERIFY_WRITE是VERIFY_READ的超集——如果写入一个块是安全的,那么从它读取总是安全的。另外,这个参数并不是一定有,这个后面的版本问题会提到。
addr:要检查的块的开始的用户空间指针
size:要检查的块的大小
返回值:此函数检查用户空间中的内存块是否可用。如果可用,则返回真(非0值),否则返回假 (0) 。
2. 头文件包含
- uaccess.h - arch/arm/include/asm/uaccess.h:体系结构相关的头文件位于内核源码的 arch/< architecture >/include/asm/ 目录。
1 |
- uaccess.h - include/asm-generic/uaccess.h:通用体系结构头文件位于 include/asm-generic/(不依赖特定 CPU)。
1 |
虽然实现有些区别,但是功能都是一样的,那么我们写代码的时候要怎么包含头文件?其实写代码的时候要包含的是这个:uaccess.h - include/linux/uaccess.h,在这个头文件中,并没有实现这个宏,但是它包含了对应的头文件:

所以在驱动中使用的时候包含下边这个头文件即可:
1 |
3. 版本问题
3.1 access_ok()在不同内核版本中的定义
其实这里有个坑,那就是函数的参数问题,在一些内核版本中是没有type参数的,例如:uaccess.h - arch/arm/include/asm/uaccess.h - Linux source code v5.0-rc1:
1 |
在uaccess.h - arch/arm/include/asm/uaccess.h - Linux source code v4.20.17中是这样的:
1 |
我看了一下,kernel官网的版本,到写这个笔记为止,v4.x版本中最新的是v4.20.17,还是三个参数,到了v5.x版本,最早的一个是v5.0-rc1,在5.0的版本中已经都是2个参数了。
3.2 怎么兼容不同的内核源码?
那我们写的驱动最好是要兼容不同的内核源码,那这里怎么兼容?要是可以获取到内核源码的版本就好了,内核中已经为我们提供了相应的版本号,是定义在linux源码 include/generated/uapi/linux/version.h 文件中的。但是下载linux kernel 源码中是么有这个文件???它其实是在编译过程中生成,在 Makefile 中生成的规则如下:
1 | define filechk_version.h |
可以看到,这里其实为我们提供了两个宏定义:
1 |
LINUX_VERSION_CODE是根据Makefile中的版本号计算出来的当前linux内核源码的版本,KERNEL_VERSION是给我们用来计算某个版本的LINUX_VERSION_CODE的宏,比如我们可以在内核中这样使用:
1 |
|
使用的时候需要包含一个头文件:linux/version.h,具体呢,我也没找到,没有深究了,知道可以这样用就行了:
1 |
二、分支预测
1. likely/unlikely
现在的 CPU 都有 ICache 和流水线机制。 即运行当前指令时, ICache 会预读取后面的指令,从而提升效率。 但是如果条件分支的结果是跳转到了其他指令, 那预取下一条指令就浪费时间了。 这里用到的 likely 和 unlikely 宏, 会让编译器总是将大概率执行的代码放在靠前的位置, 从而提高驱动的效率。
这两个宏定义在 compiler.h - include/linux/compiler.h 中:
1 |
__builtin_expect()的作用是告知编译器预期表达式 exp 等于 c 的可能性更大, 编译器可以根据该因素更好的对代码进行优化, 所以 likely 与 unlikely 的作用就是表达性 x 为真的可能性更大( likely) 和更小(unlikely) 。
2. 使用示例
这里以添加传递地址检测内容后的代码为例, 对 copy_from_user 函数添加分支预测优化函数, 添加完成如下所示:
1 | case CMD_TEST3: |
传递地址检测成功之后才会使用执行 copy_from_user 函数, 在传递地址正确的前提下copy_from_user 函数运行失败为小概率事件, 所以这里使用 unlikely 函数进行驱动效率的优化。
三、代码优化demo
demo可以看这里:10_driver_debug/01_optimizedcode · 苏木/imx6ull-driver-demo - 码云 - 开源中国