You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何访问结构体中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驱动的卸载逻辑对比,避免遗漏。
  • 对比原始驱动的事件处理逻辑
    查看系统原始PCSPKR驱动的pcspkr_event函数和卸载流程,确认是否在卸载时做了事件屏蔽操作,比如禁用设备的事件处理能力,或者在卸载前停止所有 pending 的事件请求。

内容的提问来源于stack exchange,提问作者Traveller

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 14:54:22