无法卸载内核扩展; 类具有实例

pje*_*pje 10 macos kernel kernel-module kernel-extension

我正在为音频设备驱动程序(它的软件,但模拟硬件设备)编写OSX内核扩展.

在开发过程中,完全卸载现有的旧版本然后从头开始构建和安装新版本会很方便.但是,如果没有重新启动系统,有时似乎无法进行此操作.

程序本身没有运行,源文件已从/System/Library/Extensions/目录中删除.

kextstat揭示了一个实例:

$ kextstat | grep 'com.foo.driver.bar'
219 0 0xfff123 0x5000 0x5000 com.foo.driver.bar (0.0.1) <102 5 4 3>
Run Code Online (Sandbox Code Playgroud)

(...含义:)

Index Refs Address Size Wired Name (Version) <Linked Against>
Run Code Online (Sandbox Code Playgroud)

所以我的驱动程序实例有0个参考,但kextunload有时会失败,抱怨现有的实例:

$ sudo kextunload -b com.foo.driver.bar
(kernel) Can't unload kext com.foo.driver.bar; classes have instances:
(kernel)     Kext com.foo.driver.bar class FooBarDriver has 1 instance.
(kernel)     Kext com.foo.driver.bar class com_foo_driver_bar has 1 instance.
Failed to unload com.foo.driver.bar - (libkern/kext) kext is in use or retained (cannot unload).
Run Code Online (Sandbox Code Playgroud)

当发生这种情况时,没有办法"强制"卸载kext(我知道).

我是否正确地猜测这个单个实例仍然存在,因为运行的OS内核在内存中保存了引用?这似乎不对,因为kextunload那样总会失败.那么为什么kextunload有时只需要重新启动系统来"完全"卸载所有驱动程序实例?

pmd*_*mdj 11

运行kextunloadIOKit kext将(如果没有其他kexts依赖它)导致内核尝试terminate()在I/O Kit注册表中的该kext中的任何类实例.然后它会稍等一下,检查一下这个kext的类是否还有实例.如果没有,它将卸载kext.如果实例仍然存在,kextunload则会失败(终止的实例会被终止;但是,我的意思是I/O工具包匹配不会在其提供者上重新运行).

所以,不知何故,你仍然以实时实例结束.

  • 一种可能性是你的对象拒绝terminate().如果它们具有不会放弃控制的客户端,则会发生这种情况,例如,您无法卸载顶部装有文件系统的磁盘的驱动程序.另一个例子是不响应终止消息的用户空间客户端.

  • 否则,实例终止,但不会被释放.因为它们似乎是你的两个主要驱动程序类,如果你没有任何用户客户端不会放弃他们的声明,我会打算走出困境并建议你可能有一个循环引用.如果不是这样,你只需要寻找retain()与a不匹配的s release().我在这个答案中提供了一些如何跟踪这些内容的技巧.

如果实例终止并取消注册,它们将不再出现在ioreg命令行工具的输出中,因此这是一种简单的方法,可以检查这两种情况中的哪一种适用于此处.