技术知识库
模块已加载但字符设备没有保留
模块已加载但字符设备没有保留
摘要
模块曾能在 lsmod 中出现,但 /proc/devices 没有 martin_mailbox,卸载时又出现 kobject_put() 未初始化警告。根因是初始化成功路径继续落入清理标签,提前执行了 cdev_del() 和 unregister_chrdev_region();卸载时再次删除同一个动态 cdev,形成重复释放。成功路径在错误标签之前明确 return 0 后问题消失。
问题/背景
期望 insmod 后同时满足:模块存在、字符设备号保持注册、卸载时只释放一次资源。
环境
目标机:QEMU ARM64
内核:Linux 5.0.0+
模块:martin_mailbox.ko
实际现象
lsmod |
grep martin_mailbox
grep martin_mailbox /proc/devices
lsmod 有模块,/proc/devices 却没有对应设备。随后 rmmod 报告:
kobject: '' (...): is not initialized, yet kobject_put() is being called.
Call trace:
kobject_put
cdev_del
martin_mailbox_exit
根因
C 标签不是独立作用域或自动错误分支。若初始化成功后没有 return 0,控制流会顺序执行紧随其后的清理标签:
/* 错误结构 */
cdev_add(...);
cleanup:
cdev_del(...);
unregister_chrdev_region(...);
return ret;
于是模块虽然返回成功并保留在 lsmod,设备号和 cdev 已被清掉;退出函数又清一次,产生警告。
最终修复
成功路径在错误标签之前结束;只有实际失败才 goto 清理:
ret = cdev_add(martin_mailbox_cdev, mailbox_dev, device_count);
if (ret)
goto delete_cdev;
/* class_create()、device_create() 与成功日志 */
return 0;
delete_cdev:
cdev_del(martin_mailbox_cdev);
unregister_chrdev:
unregister_chrdev_region(mailbox_dev, device_count);
return ret;
退出路径保持逆序释放,每项只执行一次:
device_destroy → class_destroy → cdev_del → unregister_chrdev_region
验证结果
修复后:
247 martin_mailbox
/proc/devices 能看到设备;/dev/martin_mailbox 正常出现;重新加载和卸载不再因本模块的重复 cdev_del() 触发上述 kobject_put() 警告。
证据与来源
- 本轮原始内核日志中的
kobject_put → cdev_del → martin_mailbox_exit调用栈。 - 修复后的项目主线:实现可读写的 martin_mailbox 字符设备。
关联
待验证
- 第 6 章结束后结合
cdev_add()、cdev_del()和 kobject 引用计数源码复盘生命周期。