Linux字符设备操作失败及卸载后设备项残留问题求助
字符设备驱动问题分析与修正方案
问题1:设备节点操作报错的原因
- 仅注册了主设备号,但未完成
cdev结构体与设备号(主+次)的绑定及内核添加操作,导致内核无法将设备节点的操作映射到驱动的file_operations - 驱动的
file_operations未正确赋值,或cdev_init调用时传入的指针错误 - 未创建设备类与设备实例,内核设备模型中无该设备记录(即使手动
mknod,也无法关联到驱动)
问题2:卸载模块后/proc/devices残留的原因
- 模块退出函数未调用
unregister_chrdev_region()释放已分配的设备号 - 未调用
cdev_del()删除已添加的cdev结构体,内核仍认为设备存在 - 退出流程顺序错误,遗漏关键资源释放步骤
修正后的完整驱动代码
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #define DEVICE_NAME "mycdev" #define MINOR_COUNT 1 // 支持的次设备号数量 static int mycdev_major; static struct cdev mycdev_cdev; static struct class *mycdev_class; // 设备打开操作 static int mycdev_open(struct inode *inode, struct file *file) { printk(KERN_INFO "mycdev opened\n"); return 0; } // 设备释放操作 static int mycdev_release(struct inode *inode, struct file *file) { printk(KERN_INFO "mycdev released\n"); return 0; } // 设备操作集合 static const struct file_operations mycdev_fops = { .owner = THIS_MODULE, .open = mycdev_open, .release = mycdev_release, }; static int __init mycdev_init(void) { int ret; dev_t dev_num; // 动态分配设备号(优先推荐,避免静态分配冲突) ret = alloc_chrdev_region(&dev_num, 0, MINOR_COUNT, DEVICE_NAME); if (ret < 0) { printk(KERN_ERR "Failed to allocate chrdev region\n"); return ret; } mycdev_major = MAJOR(dev_num); printk(KERN_INFO "Allocated major number: %d\n", mycdev_major); // 初始化cdev并绑定操作集合 cdev_init(&mycdev_cdev, &mycdev_fops); mycdev_cdev.owner = THIS_MODULE; // 将cdev添加到内核,建立设备号与驱动的关联 ret = cdev_add(&mycdev_cdev, dev_num, MINOR_COUNT); if (ret < 0) { printk(KERN_ERR "Failed to add cdev\n"); unregister_chrdev_region(dev_num, MINOR_COUNT); return ret; } // 创建设备类,让udev自动生成设备节点 mycdev_class = class_create(THIS_MODULE, DEVICE_NAME); if (IS_ERR(mycdev_class)) { printk(KERN_ERR "Failed to create class\n"); cdev_del(&mycdev_cdev); unregister_chrdev_region(dev_num, MINOR_COUNT); return PTR_ERR(mycdev_class); } // 创建设备实例,生成/dev/mycdev1节点 device_create(mycdev_class, NULL, dev_num, NULL, "%s1", DEVICE_NAME); printk(KERN_INFO "mycdev driver loaded\n"); return 0; } static void __exit mycdev_exit(void) { // 按顺序释放资源:设备实例→设备类→cdev→设备号 device_destroy(mycdev_class, MKDEV(mycdev_major, 0)); class_destroy(mycdev_class); cdev_del(&mycdev_cdev); unregister_chrdev_region(MKDEV(mycdev_major, 0), MINOR_COUNT); printk(KERN_INFO "mycdev driver unloaded\n"); } module_init(mycdev_init); module_exit(mycdev_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("My Character Device Driver"); MODULE_AUTHOR("Your Name");
关键修正要点
- 设备号管理:
- 用
alloc_chrdev_region()动态分配设备号,避免静态分配的冲突问题 - 退出时必须调用
unregister_chrdev_region()释放设备号,确保/proc/devices清理干净
- 用
- cdev关联:
- 通过
cdev_init()绑定file_operations,cdev_add()将设备号与驱动关联,让内核能识别设备节点的操作请求 - 退出时调用
cdev_del()删除cdev,告知内核设备已移除
- 通过
- 设备模型集成:
- 创建设备类和实例,让udev自动生成设备节点,无需手动
mknod(若坚持手动操作,需确保主/次设备号与驱动分配的一致)
- 创建设备类和实例,让udev自动生成设备节点,无需手动
- 错误回滚:
- 每一步操作失败后,回滚已分配的资源,避免内存泄漏或残留注册信息
内容的提问来源于stack exchange,提问作者Arvin
相关产品推荐
相关产品推荐

