OSX Kext未正确释放

时间:2012-11-19 10:08:19

标签: macos memory-management device-driver kernel-extension

我为热插拔SCSI设备编写了一个设备驱动程序kext,基于Wagerlabs code(使用驱动程序用户客户端应用程序模型),一切正常。唯一剩下的问题是驱动程序似乎不会一直被释放,特别是如果应用程序崩溃。例如,当我尝试卸载kext时,即使设备已断开连接并且应用程序已关闭,仍然存在驱动程序和用户客户端的未完成实例(驱动程序通常超过用户客户端)。

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

1 个答案:

答案 0 :(得分:4)

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

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

最后,一种用于调试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()taggedRelease()retain()release()的实际底层实现 - 如果你覆盖后者,你将看不到任何保留和来自OSCollections的版本,因为它们使用标记版本(带有非null标记)。

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

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