Implementieren abbrechbarer Syscalls im Benutzerbereich

8

Ich arbeite an der Implementierung von pthread cancellation unter Linux ohne das "unangenehme Verhalten" (einige sagen Bugs), das in einigen meiner letzten Fragen diskutiert wurde. Der Linux / glibc-Ansatz zur Entfernung von pthread war bislang so, dass er als etwas behandelt wird, das keine Kernel-Unterstützung benötigt und auf der Bibliotheksebene allein dadurch behandelt werden kann, dass die asynchrone Löschung vor dem Syscall aktiviert wird und der vorherige Löschungszustand wiederhergestellt wird nachdem der syscall zurückkehrt. Dies hat mindestens zwei Probleme, von denen eines sehr ernst ist:

  1. Abbruch kann ausgeführt werden, nachdem der syscall aus dem kernelspace zurückgekehrt ist, aber bevor userspace den Rückgabewert speichert. Dies führt zu einem Ressourcenverlust, wenn der Syscall eine Ressource zugewiesen hat, und es gibt keine Möglichkeit, ihn mit Stornierungshandlern zu patchen.
  2. Wenn ein Signal verarbeitet wird, während der Thread bei einem abbrechbaren Syscall blockiert ist, wird der gesamte Signal-Handler mit aktivierter asynchroner Aufhebung ausgeführt. Dies könnte extrem gefährlich sein, da der Signalhandler Funktionen aufrufen kann, die async-signalsicher aber nicht async-cancel-safe sind.

Meine erste Idee zur Behebung des Problems bestand darin, ein Flag zu setzen, dass der Thread an einem Cancellation-Punkt ist, anstatt Async-Cancellation zu aktivieren. Wenn dieses Flag gesetzt ist, überprüft der Cancellation-Signal-Handler den gespeicherten Befehlszeiger Es zeigt auf eine syscall-Anweisung (arch-spezifisch). Wenn dies der Fall ist, zeigt dies an, dass der Syscall nicht abgeschlossen wurde und bei Rückkehr des Signalhandlers neu gestartet würde, so dass wir abbrechen können. Wenn nicht, nahm ich an, dass der Syscall bereits zurückgekommen war, und dass die Kündigung verschoben wurde. Es gibt jedoch auch eine Race-Bedingung - es ist möglich, dass der Thread den syscall-Befehl überhaupt noch nicht erreicht hat. In diesem Fall könnte der syscall blockieren und niemals auf den Abbruch reagieren. Ein weiteres kleines Problem besteht darin, dass nicht abbrechbare Syscalls, die von einem Signal-Handler ausgeführt wurden, fälschlicherweise gelöscht werden konnten, wenn das Flag für den Löschpunkt beim Eingeben des Signal-Handlers gesetzt wurde.

Ich schaue mir einen neuen Ansatz an und suche nach einem Feedback dazu. Die Bedingungen, die erfüllt sein müssen:

  • Jede Abbruchanforderung, die vor dem Abschluss des Syscalls empfangen wird, muss bearbeitet werden, bevor der Syscall für ein signifikantes Zeitintervall blockiert, aber nicht, während ein Neustart aufgrund einer Unterbrechung durch einen Signalhandler ansteht.
  • Jede Kündigungsanforderung, die nach Abschluss des Systemaufrufs empfangen wird, muss auf den nächsten Kündigungszeitpunkt verschoben werden.

Die Idee, die ich mir vorstelle, erfordert eine spezielle Assembly für den abbrechbaren Syscall-Wrapper. Die Grundidee wäre:

  1. Drücken Sie die Adresse der anstehenden syscall-Anweisung auf den Stack.
  2. Speichern Sie den Stapelzeiger im thread-lokalen Speicher.
  3. Testen Sie ein Abbruch-Flag aus Thread-lokalem Speicher; springt zur Abbruchroutine, wenn sie eingestellt ist.
  4. Machen Sie den Systemaufruf.
  5. Löschen Sie den im lokalen Threadspeicher gespeicherten Zeiger.

Der Abbruchvorgang würde dann beinhalten:

  1. Legen Sie das Löschungskennzeichen im Thread-lokalen Speicher des Ziel-Threads fest.
  2. Testen Sie den Zeiger im Thread-lokalen Speicher des Ziel-Threads. Wenn es nicht null ist, sende ein Löschungssignal an den Ziel-Thread.

Der Stornosignal-Handler würde dann:

  1. Überprüfen Sie, ob der gespeicherte Stapelzeiger (im Signalkontext) dem gespeicherten Zeiger im threadlokalen Speicher entspricht. Wenn nicht, dann wurde der Löschpunkt von einem Signal-Handler unterbrochen und es gibt gerade nichts zu tun.
  2. Überprüfen Sie, ob das Programmzählerregister (im Signalkontext gespeichert) kleiner oder gleich der Adresse ist, die im gespeicherten Stapelzeiger gespeichert wurde. Wenn dies der Fall ist, bedeutet dies, dass der Systemaufruf noch nicht abgeschlossen ist und wir die Löschung durchführen.

Das einzige Problem, das ich bisher gesehen habe, ist in Schritt 1 des Signal-Handlers: Wenn es sich entscheidet, nicht zu handeln, dann kann der Thread nach dem Zurücksenden des Signal-Handlers auf dem Syscall blockiert werden, wobei die anstehende Abbruchanforderung ignoriert wird. Dafür sehe ich zwei mögliche Lösungen:

  1. Installieren Sie in diesem Fall einen Timer, um Signale an den spezifischen Thread zu senden, im Wesentlichen alle Millisekunden oder so, bis wir Glück haben.
  2. Heben Sie das Aufhebungssignal erneut an, kehren Sie jedoch vom Aufhebungssignal-Handler zurück, ohne das Aufhebungssignal zu demaskieren. Es wird automatisch entmaskiert, wenn der unterbrochene Signalhandler zurückkehrt, und dann können wir es erneut versuchen. Dies kann jedoch das Verhalten von Abbruchpunkten innerhalb des Signalhandlers beeinträchtigen.

Irgendwelche Gedanken darüber, welche Herangehensweise am besten ist, oder wenn es noch grundlegendere Fehler gibt, die mir fehlen?

    
R.. 16.04.2011, 03:20
quelle

1 Antwort

3

Lösung 2 fühlt sich weniger wie ein Hack an. Ich denke nicht, dass dies das von Ihnen vorgeschlagene Problem verursachen würde, da abbrechbare Syscalls, die innerhalb des syscall-Handlers aufgerufen werden, das Löschungsflag in TLS prüfen, das bereits gesetzt wurde, wenn der Cancelling-Signal-Handler sowieso mit der Signalmaske gelaufen ist.

(Es scheint, als wäre es viel einfacher für Implementierer, wenn jeder blockierende Systemaufruf einen sigmask -Parameter a la pselect() benötigt).

    
caf 16.04.2011, 13:05
quelle

Tags und Links