10 Stimmen

Kann die Kernelerweiterung nicht entladen; Klassen haben Instanzen

Ich schreibe eine OSX-Kernel-Erweiterung für einen Audiogerätetreiber (es handelt sich um Software, die jedoch ein Hardwaregerät emuliert).

Während der Entwicklung wäre es praktisch, vorhandene alte Versionen vollständig zu deinstallieren und dann die neue Version von Grund auf zu erstellen und zu installieren. Dies scheint jedoch gelegentlich ohne einen Systemneustart nicht möglich zu sein.

Das Programm selbst läuft nicht, und die Quelldateien wurden aus dem /System/Library/Extensions/-Verzeichnis gelöscht.

Aber kextstat zeigt eine einzige Instanz:

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

(...was bedeutet:)

Index Refs Adresse Größe Verdrahtet Name (Version) 

Es gibt also 0 Refs zu meiner Treiberinstanz, aber kextunload wird manchmal fehlschlagen und sich über vorhandene Instanzen beschweren:

$ sudo kextunload -b com.foo.driver.bar
(kernel) Kann kext com.foo.driver.bar nicht entladen; Klassen haben Instanzen:
(kernel)     Kext com.foo.driver.bar Klasse FooBarDriver hat 1 Instanz.
(kernel)     Kext com.foo.driver.bar Klasse com_foo_driver_bar hat 1 Instanz.
Entladen von com.foo.driver.bar fehlgeschlagen - (libkern/kext) kext wird verwendet oder beibehalten (kann nicht entladen werden).

Wenn dies passiert, gibt es keinen Weg, den kext "gewaltsam" zu entladen (den ich kenne).

Habe ich recht in der Vermutung, dass diese einzelne Instanz noch existiert, weil im laufenden Betriebssystemkern ein Verweis im Speicher gehalten wird? Das scheint nicht richtig zu sein, denn dann würde kextunload immer fehlschlagen. Warum erfordert kextunload also nur manchmal einen Systemneustart, um alle Treiberinstanzen "vollständig" zu entladen?

11voto

pmdj Punkte 19560

Das Ausführen von kextunload für einen IOKit-kext wird (falls keine anderen kexts davon abhängen) dazu führen, dass der Kernel versucht, alle Instanzen von Klassen in diesem kext, die im I/O Kit-Register sind, zu terminate(). Er wird dann kurz warten und überprüfen, ob noch Instanzen von den Klassen dieses kext vorhanden sind. Wenn nicht, wird er den kext entladen. Wenn Instanzen übrig bleiben, schlägt kextunload fehl (die terminierten Instanzen bleiben jedoch terminiert; damit meine ich, dass das I/O Kit-Matching bei ihren Providern nicht neu gestartet wird).

Also irgendwie hast du immer noch lebendige Instanzen.

  • Eine Möglichkeit ist, dass deine Objekte sich weigern zu terminate(). Dies kann passieren, wenn sie Clients haben, die die Kontrolle nicht aufgeben, z.B. kannst du den Treiber für eine Festplatte mit einem darauf gemounteten Dateisystem nicht entladen. Userspace-Clients, die nicht auf Beendigungsnachrichten reagieren, sind ein weiteres Beispiel.

  • Ansonsten terminieren die Instanzen, werden aber nicht freigegeben. Da sie anscheinend zu zwei deiner Haupt-Treiberklassen gehören, wenn du keine Benutzerclients hast, die ihren Anspruch nicht aufgeben, gehe ich auf ein Risiko ein und behaupte, dass du möglicherweise eine zirkuläre Referenz hast. Falls das nicht der Fall ist, musst du einfach nach retain()-Aufrufen suchen, die nicht durch ein release() abgeglichen sind. Ich gebe einige Tipps, wie du diese finden kannst, in dieser Antwort.

Wenn die Instanzen terminiert und deregistriert sind, werden sie nicht mehr im Output des Befehlszeilentools ioreg erscheinen, also ist das eine einfache Möglichkeit zu überprüfen, welcher der beiden Fälle hier zutrifft.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X