4

我已经为热插拔 SCSI 设备编写了一个设备驱动程序 kext,在某种程度上基于Wagerlabs 代码(使用驱动程序-用户客户端-应用程序模型)并且一切正常。唯一剩下的问题是驱动程序似乎没有被持续释放,尤其是在应用程序崩溃的情况下。例如,当我尝试卸载 kext 时,即使设备断开连接并且应用程序关闭,驱动程序和用户客户端的未完成实例仍然存在(驱动程序的数量通常超过用户客户端)。

我已经登录了类似的驱动程序功能free(),当我关闭计算机时,我可以看到这些正在执行,因此显然仍然可以终止实例。什么是确保驱动程序实例被终止和释放的“正确”方法,即使主机应用程序崩溃、不正确终止或事情通常不按计划进行?

4

1 回答 1

5

如果您在没有用户客户端应用程序运行时拥有用户客户端类实例,那么您肯定会更频繁地保留用户客户端实例而不是发布它们。例如,您可能会在主驱动程序类中保留对客户端实例的保留引用。在您的用户客户端类的stop()方法中,确保从驱动程序中删除该客户端实例。

另一件需要注意的事情:确保从内置 IOService 方法(例如 等)的覆盖版本调用超类实现stop()free()不这样做通常会使 IO Kit 进入不一致的状态。

最后,在 I/O Kit 驱动程序中调试保留泄漏的一个有用技术是通过使用日志版本覆盖方法来实际记录保留和释放:

void MyClass::taggedRetain(const void* tag) const
{
    OSReportWithBacktrace(
        "MyClass" CLASS_OBJECT_FORMAT_STRING "::taggedRetain(tag=%p)\n", CLASS_OBJECT_FORMAT(this), tag);
    IOService::taggedRetain(tag);
}
void MyClass::taggedRelease(const void * tag) const
{
    OSReportWithBacktrace(
        "MyClass" CLASS_OBJECT_FORMAT_STRING "::taggedRelease(tag=%p)\n", CLASS_OBJECT_FORMAT(this), tag);
    int count = getRetainCount();
    IOService::taggedRelease(tag);
    if (count == 1)
        printf(
            "MyClass::taggedRelease(tag=%p) final done\n", tag);
    else
        printf(
            "MyClass" CLASS_OBJECT_FORMAT_STRING "::taggedRelease(tag=%p) done\n", CLASS_OBJECT_FORMAT(this), tag);
}

此代码中的宏在标头中定义如下:

#define CLASS_OBJECT_FORMAT_STRING "[%s@%p:%dx]"
#define CLASS_OBJECT_FORMAT(obj) myClassName(obj), obj, myRefCount(obj)

inline int myRefCount(const OSObject* obj)
{
    return obj ? obj->getRetainCount() : 0;
}

inline const char* myClassName(const OSObject* obj)
{
    if (!obj) return "(null)";
    return obj->getMetaClass()->getClassName();
}
#endif

我应该解释一下taggedRetain()and是andtaggedRelease()的实际底层实现- 如果您覆盖后者,您将不会看到来自 OSCollections 的任何保留和释放,因为它们使用标记版本(带有非空标记)。retain()release()

不幸的是,生成的回溯OSReportWithBacktrace()只是一堆十六进制指针,但您可以使用 gdb 查找它们。

在任何情况下,通过记录对象的保留和释放,您可以检查所有保留并确保它们与正确位置的释放相匹配。注意循环!

于 2012-11-20T10:44:07.473 回答