公司动态

驱动的编写

📅 2026/8/12 15:34:40
驱动的编写
一、内核编程与应用编程的差异开发驱动程序需要解决 3 个核心问题如何在已经运行的操作系统内核中执行我们自己编写的驱动代码掌握驱动代码的标准编写框架驱动最终是对硬件寄存器进行操作原理和裸机编程一致但内核不能直接访问物理地址需要完成虚拟地址和物理地址的映射Linux 驱动分为两种加载模式静态加载编译进 zImage 内核镜像、动态加载生成 ko 模块。1.1 静态加载驱动编译进 zImage把驱动直接编译进内核 zImage 镜像编写驱动.c源文件准备编译相关文件.config、Kconfig、Makefile执行make menuconfig图形配置界面勾选驱动选项*, 代表编译进内核重新编译生成 zImage 镜像镜像体积会增大驱动成为内核的一部分将新zImage烧录开发板开发板重启后新增驱动代码方可生效缺点每次修改驱动都需要重新编译内核、烧写镜像必须重启开发板调试效率低。1.2 动态加载生成 ko 模块将驱动编译为独立的.ko模块文件独立于zImage系统启动后可以手动加载卸载调试灵活。编写驱动.c源文件修改 Makefileobj‑m led.o‑m表示编译为外部 ko 模块执行make modules编译模块生成.ko文件开发板 Linux 系统运行后执行insmod xxx.ko手动加载驱动执行rmmod xxx卸载模块无需重启开发板ko 不属于 zImage 的一部分可以按需加载卸载。常用模块操作命令insmod myhell.ko# 加载内核模块lsmod# 查看已经加载的内核模块rmmod myhell# 卸载内核模块ls/sys/module# 查看sysfs下模块相关信息二、字符设备重要内核宏与函数编写最简驱动模块需要掌握如下内核接口二、 字符设备重要内核宏与函数函数 / 宏功能说明module_init()驱动模块加载入口宏执行 insmod 时调用该函数module_exit()驱动模块卸载入口宏执行 rmmod 时调用该函数MODULE_LICENSE(“GPL”)模块许可证声明必须添加否则内核产生污染警告printk()内核打印函数类似用户态 printf不支持浮点数输出输出等级受内核配置控制pr_info(fmt,…)带输出等级打印输出普通日志信息pr_err(fmt,…)带输出等级打印输出错误日志信息前置开发环境准备配置好 tags、cscope 源码索引完成 vim/vscode 自动补全环境。三、方式一在内核源码目录内编写驱动在drivers/char目录新增驱动文件借助内核Kconfig/Makefile编译体系完成编译。3.1 创建驱动源码在drivers/char目录新建mydevtest.c#includelinux/printk.h#includelinux/init.h#includelinux/module.hstaticint__initmymodule_init(void){pr_info(im coming…,mymodule_init ok\n);return0;}staticvoid__exitmymodele_exit(void){pr_info(mymodule_init byebye\n);return;}module_init(mymodule_init);module_exit(mymodele_exit);MODULE_LICENSE(GPL);__init标记初始化函数内核启动完成后该段内存可以被释放 __exit标记卸载函数如果驱动静态编译进内核该函数会被丢弃3.2 修改同目录 Kconfig在 drivers/char/Kconfig 中添加驱动配置项Kconfigconfig MYMODULE tristatemymodule inner kernel testdefault yhelpdrivertestkconfig makefiletristate 为三态选项Y 编译进内核、M 编译成 ko 模块、N 不编译config MYMODULE 定义配置变量供 Makefile 使用。3.3 修改同目录 Makefile在 drivers/char/Makefile 增加下面一行makefileobj-$(CONFIG_MYMODULE)myhello.o CONFIG_MYMODULE 由 menuconfig 的选择决定 选择 Y展开为obj-ymyhello.o编译进 zImage 选择 M展开为obj-mmyhello.o编译生成 ko 模块 选择 N该行直接忽略不编译该文件3.4 编译模块开发板加载测试执行make menuconfig找到mymodule inner kernel test配置项选择 M编译模块执行make modules编译模块生成myhello.ko将 ko 文件拷贝至开发板 nfs 根文件系统开发板终端执行命令bashinsmod myhell.ko lsmod rmmod myhello控制台输出对应打印信息代表模块加载卸载正常。四、方式二内核源码目录外搭建独立驱动工程实际项目普遍采用该方案驱动代码完全独立于内核源码树不需要修改内核内部 Kconfig、Makefile。前置条件内核源码已经完整编译完成。4.1 工程目录结构plaintextproject/├── app/ # 用户态测试应用程序│ ├── app.c│ └── Makefile├── drv/ # 内核驱动代码│ ├── drv.c│ ├── Makefile│ └── .clangd # vscode自动补全配置文件└── Makefile # 顶层总Makefile一键编译app和驱动drv 目录 Makefile驱动模块编译#makefileMODULE_NAME:drv 修改为你本机已编译完成内核源码的绝对路径KER_DIR :/home/linux/imx6ull/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek obj-m :$(MODULE_NAME).o all:make-C$(KER_DIR)M$(CUR_DIR)modules clean:make-C$(KER_DIR)M$(CUR_DIR)clean distclean:rm-f*.ko *.o *.mod *.mod.c *.symvers *.ordermake-C$(KER_DIR)M$(CUR_DIR)distclean注意MODULE_NAME:drv 赋值等号后面不要加多余空格极易引发编译问题。#makefileSRC:app.c OBJ:app修改为你的交叉编译器CC:arm-linux-gnueabihf-gcc顶层总 Makefile一键编译全部工程all:$(CC)$(SRC)-o$(OBJ)clean:rm$(OBJ)bash makefile all:make-C./app allmake-C./drv all clean:make-C./app cleanmake-C./drv clean4.2 编译与开发板使用在工程顶层目录执行make同时编译用户应用和驱动模块得到drv.ko与app可执行程序将 drv.ko、app 拷贝到开发板。开发板终端执行bashinsmod drv.ko ./app rmmod drv五、两种驱动开发方案对比总结内核源码树内写驱动需要修改内核源码内部的 Kconfig、Makefile适合需要合入主线内核的驱动调试繁琐每次修改需要关注 menuconfig 配置。内核源码树外独立工程驱动代码完全独立不污染内核源码只需要一份编译完成的内核源码修改驱动只编译自身工程推荐日常驱动调试使用.