嵌入式 Linux Hello 驱动

张开发
2026/4/19 4:09:22 15 分钟阅读

分享文章

嵌入式 Linux Hello 驱动
一、前言本文基于韦东山 IMX6ULL 开发板从零实现最简 Linux 字符设备驱动Hello 驱动包含原理流程、架构说明、内核函数详解、全套带注释源码、Makefile 配置、编译报错排查、开发板加载测试全流程直接适配 CSDN 博客发布。二、整体开发环境项目详细信息硬件韦东山 IMX6ULL 开发板开发主机Ubuntu 虚拟机内核源码/home/book/100ask_imx6ull-sdk/Linux-4.9.88交叉编译器gcc-linaro 6.2.1arm-linux-gnueabihf调试方式ADB/NFS 文件传输 串口终端三、驱动整体运行架构 流程图解1、三层交互架用户空间hello_drv_test ↓ open/read/write 内核VFS虚拟文件系统 ↓ 匹配调用 驱动层hello_drv.c 内核模块 ↓ /dev/hello 设备节点访问入口2、驱动加载流程insmod hello_drv.ko加载模块自动执行module_init入口函数注册字符设备 → 创建设备 class → 自动生成 /dev/hello 节点等待用户程序读写访问3、读写交互流程应用 open/dev/hello→ 触发驱动open函数应用 write 写入字符串 →copy_from_user拷贝到内核缓冲区应用 read 读取数据 →copy_to_user把内核数据发给应用应用 close → 触发驱动 release 函数四、核心内核 API 详解新手必看1、注册 / 注销字符设备register_chrdev 注册驱动int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops);major主设备号填 0 让内核自动分配name驱动名称会展示在/proc/devicesfops绑定 open/read/write 回调函数结构体unregister_chrdev 注销驱动卸载驱动时必须调用释放内核资源。2、自动创建设备节点相关class_create / class_destroy创建 / 销毁设备类是自动生成/dev/xxx的前提。device_create / device_destroy根据设备类 设备号自动在/dev目录生成设备文件。MKDEV(major,0)拼接主、次设备号。3、内核 用户空间数据拷贝重中之重copy_from_user 用户→内核把 APP 写入的数据安全拷贝到内核缓冲区禁止直接 memcpy。copy_to_user 内核→用户把内核缓存数据安全发给应用层。原理内核空间与用户空间地址隔离专用函数会做权限校验、地址合法性检查。五、全套带超详细注释 驱动源码 hello_drv.c#include linux/module.h // 模块基础入口/出口声明 #include linux/fs.h // 文件操作、字符设备注册 #include linux/errno.h // 错误码定义 #include linux/kernel.h // printk内核打印 #include linux/init.h // __init __exit 宏 #include linux/device.h // class/device创建设备节点 // 全局变量 static int major 0; // 主设备号0自动分配 static char kernel_buf[1024]; // 内核数据缓存 static struct class *hello_class; // 设备类指针 // 取最小值防止缓冲区溢出 #define MIN(a, b) (a b ? a : b) // 打开设备触发 static int hello_drv_open(struct inode *node, struct file *file) { printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__); return 0; } // 读设备内核数据发给APP static ssize_t hello_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset) { printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__); // 内核 - 用户空间 copy_to_user(buf, kernel_buf, MIN(1024, size)); return MIN(1024, size); } // 写设备APP数据传给内核 static ssize_t hello_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset) { printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__); // 用户空间 - 内核 copy_from_user(kernel_buf, buf, MIN(1024, size)); return MIN(1024, size); } // 关闭设备触发 static int hello_drv_close(struct inode *node, struct file *file) { printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__); return 0; } // 绑定所有操作函数到内核 static struct file_operations hello_drv { .owner THIS_MODULE, .open hello_drv_open, .read hello_drv_read, .write hello_drv_write, .release hello_drv_close, }; // 驱动入口insmod执行 static int __init hello_init(void) { // 1.注册字符设备 major register_chrdev(0, hello, hello_drv); // 2.创建设备类 hello_class class_create(THIS_MODULE, hello_class); if(IS_ERR(hello_class)) { unregister_chrdev(major, hello); return -1; } // 3.自动创建 /dev/hello device_create(hello_class, NULL, MKDEV(major, 0), NULL, hello); return 0; } // 驱动出口rmmod执行 static void __exit hello_exit(void) { // 倒序释放资源 device_destroy(hello_class, MKDEV(major, 0)); class_destroy(hello_class); unregister_chrdev(major, hello); } // 声明入口、出口 module_init(hello_init); module_exit(hello_exit); // 必须加GPL协议否则内核污染警告 MODULE_LICENSE(GPL);六、适配 IMX6ULL 专用 Makefilemakefile# 内核源码路径必须改成自己板子真实路径 KERN_DIR /home/book/100ask_imx6ull-sdk/Linux-4.9.88 # IMX6ULL架构交叉编译器 ARCH arm CROSS_COMPILE arm-linux-gnueabihf- PWD : $(shell pwd) # 编译为内核模块 obj-m hello_drv.o all: make -C $(KERN_DIR) M$(PWD) modules $(CROSS_COMPILE)gcc -o hello_drv_test hello_drv_test.c clean: make -C $(KERN_DIR) M$(PWD) modules clean rm -rf modules.order Module.symvers hello_drv_test七、编译前环境配置关键终端执行临时配置交叉编译器环境export PATH$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin export ARCHarm export CROSS_COMPILEarm-linux-gnueabihf- # 验证是否生效 arm-linux-gnueabihf-gcc -v输出版本号即可执行make编译成功生成hello_drv.ko驱动模块hello_drv_test应用测试程序八、实操遇到的报错 根治方案报错 1内核路径不存在plaintextNo such file or directory原因Makefile 里写了 RK3399/STM32MP157 的内核路径和 IMX6ULL 不匹配。解决ls /home/book/100ask_imx6ull-sdk/Linux-4.9.88确认路径修改 Makefile 中KERN_DIR为真实路径报错 2arm-linux-gnueabihf-gcc: not found原因没配置交叉编译器环境变量。解决执行上面环境配置三条 export 命令再重新 make。报错 3adb push hello_drv 提示找不到文件原因驱动文件是hello_drv.ko少写后缀.ko。正确命令bash运行adb push hello_drv.ko hello_drv_test /home/book/nfs_rootfs/九、开发板端加载 测试全程命令1、进入 NFS 共享目录cd /home/book/nfs_rootfs ls2、加载驱动insmod hello_drv.ko3、查看驱动是否注册cat /proc/devices | grep hello ls /dev/hello -l4、功能读写测试# 写入字符串 ./hello_drv_test -w wiki.100ask.net # 读取字符串 ./hello_drv_test -r5、卸载驱动rmmod hello_drv十、新手永久避坑总结不同开发板KERN_DIR 必须严格对应不能抄别人的编译嵌入式驱动必须配置交叉编译器环境内核和用户数据交换只能用copy_from_user/copy_to_user驱动模块一定带.ko后缀传输、加载不能漏卸载命令是rmmod不要写成 rrmod。

更多文章