为何访问结构体中const char*的printk会阻止pcspkr驱动卸载?
PCSPKR驱动卸载时空指针解引用的排查思路
问题场景
修改PCSPKR驱动的pcspkr_event函数,添加dev->name打印后,执行echo -e \\a能正常输出日志,但卸载模块时控制台输出"Killed",dmesg显示空指针解引用,且pcspkr_event被第四次触发;移除dev->name打印行后,驱动可正常卸载。错误发生后重新加载驱动会导致控制台冻结,rmmod -f提示设备或资源忙。
排查思路
检查input_dev的生命周期管理
模块卸载时,input_dev可能已经被输入子系统销毁或释放,此时dev指针变为空指针/野指针。需要确认:- 模块卸载函数中是否按正确顺序调用
input_unregister_device()和input_free_device()——必须先注销设备,再释放内存。 - 在卸载函数中添加打印,记录
input_dev的地址,对比pcspkr_event中dev的地址,验证卸载时该指针是否已失效。
- 模块卸载函数中是否按正确顺序调用
定位event函数第四次调用的来源
卸载过程中触发event函数,说明仍有事件在投递。可以:- 用
lsof /dev/input/eventX(X为PCSPKR对应的event设备编号)检查是否有用户态进程未关闭设备文件描述符。 - 在
pcspkr_event中添加dump_stack()打印调用栈,明确第四次调用的发起路径,判断是输入子系统的延迟工作、还是用户态残留请求。
- 用
添加空指针防护逻辑
即使event函数被意外调用,也要避免非法内存访问。修改代码:static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { static int number_of_calls = 0; printk(KERN_DEBUG "Starting to beep! This is beep number %d.\n", ++number_of_calls); if (dev) { printk(KERN_DEBUG "Input device name: %s\n", dev->name); } else { printk(KERN_DEBUG "Input device pointer is NULL!\n"); dump_stack(); } return 0; }也可以用
input_device_unregistered(dev)宏判断设备是否已注销,再决定是否访问成员。修复卸载后的资源泄漏问题
崩溃会导致IO端口、中断、input设备节点等资源未被正确释放,引发后续加载失败:- 尝试用SysRq键强制恢复:先按
Alt+SysRq+s同步磁盘,再按Alt+SysRq+u卸载挂载资源,最后尝试重新卸载模块。 - 检查模块卸载函数,确保所有申请的资源都被释放:比如
release_region()释放IO端口、free_irq()释放中断等,与原始PCSPKR驱动的卸载逻辑对比,避免遗漏。
- 尝试用SysRq键强制恢复:先按
对比原始驱动的事件处理逻辑
查看系统原始PCSPKR驱动的pcspkr_event函数和卸载流程,确认是否在卸载时做了事件屏蔽操作,比如禁用设备的事件处理能力,或者在卸载前停止所有 pending 的事件请求。
内容的提问来源于stack exchange,提问作者Traveller
相关产品推荐
相关产品推荐

