技术知识库

模块已加载但字符设备没有保留

模块已加载但字符设备没有保留

摘要

模块曾能在 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() 警告。

证据与来源

关联

待验证

  • 第 6 章结束后结合 cdev_add()、cdev_del() 和 kobject 引用计数源码复盘生命周期。