Linux创建字符设备的过程可以为以下:Linux系统下如何完整地创建一个字符设备文件?

摘要:Linux创建字符设备 驱动程序主要包括以下几个关键部分: 注册设备号和 cdev 实现 file_operations 结构体(包含 readwrite 等操作) 创建设备类和设备节点 资源释放和模块卸载 ​ file_operatio
Linux创建字符设备 驱动程序主要包括以下几个关键部分: 注册设备号和 cdev 实现 file_operations 结构体(包含 read/write 等操作) 创建设备类和设备节点 资源释放和模块卸载 ​ file_operations 结构体模板: static struct file_operations mydev_fops = { .owner = THIS_MODULE, // 模块所有者(必须填写) .open = mydev_open, // 打开操作(自己实现的函数) .release = mydev_release, // 关闭/释放 .read = mydev_read, // 读操作 .write = mydev_write, // 写操作 // 可以根据需要实现ioctl、poll等 }; 程序实践 Makefile export ARCH=arm64 #需要先设置交叉编译器环境变量(可以参考我另一篇博客https://www.cnblogs.com/tianwuyvlianshui/p/19571779) export CROSS_COMPILE=aarch64-linux-gnu- obj-m += mychardev.o KDIR := $(HOME)/Desktop/SDK/kernel PWD := $(shell pwd) all: make -C $(KDIR) M=$(PWD) \ ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules clean: make -C $(KDIR) M=$(PWD) clean mychardev.c #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/kdev_t.h> #include <linux/cdev.h> #include <linux/device.h> // 设备名和类名的宏定义 #define DEV_NAME "device_test" // 设备节点名称 #define CLASS_NAME "class_test" // 设备类名称 static dev_t dev_num; // 保存设备号 static struct cdev cdev_test; // 字符设备结构体 static struct class *class_test; // 设备类指针 // 打开设备时调用的函数 // inode: 指向文件的 inode 结构体指针 // file: 文件结构体指针 static int chrdev_open(struct inode *inode, struct file *file) { printk(KERN_INFO "mychardev: chrdev_open called\n"); // 打印打开信息到内核日志 return 0; // 返回0表示成功 } // 读设备时调用的函数 // file: 文件结构体指针 // buf: 用户空间的缓冲区指针 // size: 期望读取的字节数 // off: 偏移量指针 static ssize_t chrdev_read(struct file *file, char __user *buf, size_t size, loff_t *off) { printk(KERN_INFO "mychardev: chrdev_read called\n"); // 打印读操作信息 return 0; // 返回0表示没有数据可读 } // 写设备时调用的函数 // file: 文件结构体指针 // buf: 用户空间的缓冲区指针 // size: 要写入的字节数 // off: 偏移量指针 static ssize_t chrdev_write(struct file *file, const char __user *buf, size_t size, loff_t *off) { printk(KERN_INFO "mychardev: chrdev_write called\n"); // 打印写操作信息 return size; // 返回写入的字节数,表示写入成功 } // 关闭设备时调用的函数 // inode: 指向文件的 inode 结构体指针 // file: 文件结构体指针 static int chrdev_release(struct inode *inode, struct file *file) { printk(KERN_INFO "mychardev: chrdev_release called\n"); // 打印关闭信息 return 0; // 返回0表示成功 } // file_operations 结构体,指明本设备支持的操作 static struct file_operations cdev_fops_test = { .owner = THIS_MODULE, // 拥有者,一般为 THIS_MODULE .open = chrdev_open, // open 操作 .read = chrdev_read, // read 操作 .write = chrdev_write, // write 操作 .release = chrdev_release, // release 操作 }; // 模块加载时自动调用的初始化函数 static int __init chrdev_fops_init(void) { int ret; int major, minor; // 1. 自动申请设备号,主设备号和次设备号由内核分配 ret = alloc_chrdev_region(&dev_num, 0, 1, DEV_NAME); if (ret < 0) { printk(KERN_ERR "mychardev: alloc_chrdev_region failed\n"); // 申请失败 return ret; } major = MAJOR(dev_num); // 获取主设备号 minor = MINOR(dev_num); // 获取次设备号 printk(KERN_INFO "mychardev: alloc_chrdev_region ok: major=%d, minor=%d\n", major, minor); // 2. 初始化 cdev 结构体,并添加到内核 cdev_init(&cdev_test, &cdev_fops_test); // 初始化 cdev ret = cdev_add(&cdev_test, dev_num, 1); // 注册 cdev 到内核 if (ret < 0) { printk(KERN_ERR "mychardev: cdev_add failed\n"); unregister_chrdev_region(dev_num,1); // 失败时释放设备号 return ret; } // 3. 创建设备类,便于自动创建设备节点 class_test = class_create(THIS_MODULE, CLASS_NAME); if (IS_ERR(class_test)) { printk(KERN_ERR "mychardev: class_create failed\n"); cdev_del(&cdev_test); unregister_chrdev_region(dev_num,1); return PTR_ERR(class_test); } // 4. 创建设备节点 /dev/device_test if (device_create(class_test, NULL, dev_num, NULL, DEV_NAME) == NULL) { printk(KERN_ERR "mychardev: device_create failed\n"); class_destroy(class_test); cdev_del(&cdev_test); unregister_chrdev_region(dev_num,1); return -ENOMEM; } printk(KERN_INFO "mychardev: chrdev driver loaded successfully\n"); // 驱动加载成功 return 0; } // 模块卸载时自动调用的清理函数 static void __exit chrdev_fops_exit(void) { device_destroy(class_test, dev_num); // 删除设备节点 class_destroy(class_test); // 删除设备类 cdev_del(&cdev_test); // 注销 cdev unregister_chrdev_region(dev_num, 1); // 释放设备号 printk(KERN_INFO "mychardev: chrdev driver unloaded\n"); // 卸载信息 } // 指定模块的初始化和退出函数 module_init(chrdev_fops_init); // 加载模块时调用 module_exit(chrdev_fops_exit); // 卸载模块时调用 MODULE_LICENSE("GPL"); // 模块许可证声明 装载,查看,卸载