Wie ist der Status von asynchronen POSIX-E / A (AIO)?

93

Es gibt im Internet verstreute Seiten, die POSIX AIO-Funktionen in unterschiedlichen Details beschreiben. Keiner von ihnen ist schrecklich neu. Es ist nicht klar, was genau sie beschreiben. Auf der "offiziellen" (?) Website für die asynchrone E / A-Unterstützung des Linux-Kernels heißt es beispielsweise, dass Sockets nicht funktionieren, aber die "aio.h" -Handbuchseiten auf meiner Ubuntu 8.04.1-Workstation scheinen dies alle zu implizieren Es funktioniert für beliebige Dateideskriptoren. Dann gibt es noch ein anderes Projekt, das auf Bibliotheksebene mit noch weniger Dokumentation zu funktionieren scheint .

Ich würde gerne wissen:

  • Was ist der Zweck von POSIX AIO? Angesichts der Tatsache, dass das offensichtlichste Beispiel für eine Implementierung, die ich finden kann, besagt, dass sie keine Sockets unterstützt, erscheint mir das Ganze seltsam. Ist es nur für asynchrone Festplatten-E / A? Wenn ja, warum die hyper-allgemeine API? Wenn nicht, warum wurde die Festplatten-E / A als erstes angegriffen?
  • Wo gibt es beispielsweise vollständige POSIX AIO-Programme, die ich mir ansehen kann?
  • Verwendet es tatsächlich jemand?
  • Welche Plattformen unterstützen POSIX AIO? Welche Teile davon unterstützen sie? Unterstützt jemand wirklich das implizite "Any I / O to Any FD", <aio.h>das zu versprechen scheint?

Die anderen Multiplexing-Mechanismen, die mir zur Verfügung stehen, sind vollkommen gut, aber die zufälligen Informationsfragmente, die da draußen herumschwirren, haben mich neugierig gemacht.

Glyphe
quelle

Antworten:

27

Netzwerk-E / A hat für AIO keine Priorität, da jeder, der POSIX-Netzwerkserver schreibt, einen ereignisbasierten, nicht blockierenden Ansatz verwendet. Der Java-Ansatz "Milliarden blockierender Threads" im alten Stil ist schrecklich.

Festplattenschreib-E / A sind bereits gepuffert, und Festplattenlese-E / A können mithilfe von Funktionen wie posix_fadvise vorab in den Puffer abgerufen werden. Damit bleibt die direkte, ungepufferte Festplatten-E / A als einziger nützlicher Zweck für AIO.

Direkte, ungepufferte E / A sind nur für Transaktionsdatenbanken wirklich nützlich, und diese neigen dazu, ihre eigenen Threads oder Prozesse zu schreiben, um ihre Festplatten-E / A zu verwalten.

Am Ende bleibt POSIX AIO also in der Lage, keinen nützlichen Zweck zu erfüllen. Benutze es nicht.

Zan Lynx
quelle
8
Was ist mit Lesen / Schreiben aus Netzwerk-Dateisystemen (NFS, Samba)?
Alex B
1
Gut. Ich habe mehrere große dumme Autoren, die, wenn ich sie in den Cache lasse, das Dirty_Ratio bei Spitzenwerten treffen und alle anderen blockieren. Wenn ich nur direkte E / A auf ihnen verwende, ist es viel zu langsam. Wenn ich nur einen Thread hätte, könnte ich ihn alleine verwalten, aber es wird schwierig sein, verschiedene E / A-Prioritäten in einem Schritt zu unterstützen. AIO + CFQ wouls scheinen wirklich eine gute Kombination zu sein, wenn AIO funktioniert
n-alexander
37
Ich bin nicht einverstanden. Festplatten-E / A sind in der Regel gepuffert, können jedoch blockiert werden. Beim Abrufen () einer Datei-FD wird immer gemeldet, dass die FD lesbar ist, auch wenn sie blockiert wird. Dies macht es unmöglich, nicht blockierende Operationen an Festplattendateien auf ereignisgesteuerte Weise auszuführen, es sei denn, man verwendet Threads oder AIO.
Hongli
2
@Matt: Die Reihenfolge ist für Datagramm-Sockets nicht wichtig. @Zan: Async I / O eignet sich sehr gut zum Vorpuffern von Echtzeit-Streaming-Daten, z. B. Mediaplayern.
Ben Voigt
13
Es ist nicht wahr, dass AIO in ereignisbasierten Systemen nutzlos ist. Mit dem richtigen AIO können Sie tatsächlich zum Nullkopie-Netzwerk gelangen, was mit einer ereignisbasierten Benachrichtigung an recv () nicht möglich ist. Andere Dinge mögen sich verschwören, um dies größtenteils zu einer theoretischen Einschränkung zu machen, aber ich denke, dass das Fehlen einer richtigen AIO (a la OVERLAPPED unter Windows) eines der letzten großen Löcher in Linux ist.
Jon Watte
69

Das effiziente Ausführen von Socket-E / A wurde mit Kqueue-, Epoll-, E / A-Abschlussports und dergleichen gelöst. Das Ausführen von asynchronen Datei-E / A ist eine Art Verspätung (abgesehen von der überlappenden E / A-Unterstützung von Windows und der frühen Unterstützung von Solaris für Posix AIO).

Wenn Sie Socket-E / A ausführen möchten, ist es wahrscheinlich besser, einen der oben genannten Mechanismen zu verwenden.

Der Hauptzweck von AIO besteht daher darin, das Problem der asynchronen Festplatten-E / A zu lösen. Dies ist höchstwahrscheinlich der Grund, warum Mac OS X AIO nur für reguläre Dateien und nicht für Sockets unterstützt (da kqueue das sowieso viel besser macht).

Schreibvorgänge werden normalerweise vom Kernel zwischengespeichert und zu einem späteren Zeitpunkt gelöscht. Zum Beispiel, wenn der Lesekopf des Laufwerks zufällig an der Stelle vorbeifährt, an der der Block geschrieben werden soll.

Für Lesevorgänge ist AIO jedoch die einzige Option, wenn der Kernel Ihre Lesevorgänge priorisieren und sortieren soll. Hier ist, warum der Kernal (theoretisch) das besser kann als jede Anwendung auf Benutzerebene:

  • Der Kernel sieht alle Festplatten-E / A, nicht nur die Festplattenaufträge Ihrer Anwendungen, und kann sie auf globaler Ebene bestellen
  • Der Kernel weiß (möglicherweise), wo sich der Plattenlesekopf befindet, und kann die Leseaufträge, die Sie an ihn weitergeben, in optimaler Reihenfolge auswählen, um den Kopf um die kürzeste Strecke zu bewegen
  • Der Kernel kann die native Befehlswarteschlange nutzen , um Ihre Lesevorgänge weiter zu optimieren
  • Mit lio_listio () können Sie möglicherweise mehr Lesevorgänge pro Systemaufruf ausführen als mit readv (), insbesondere wenn Ihre Lesevorgänge nicht (logisch) zusammenhängend sind, wodurch ein winziger Teil des Systemaufrufaufwands eingespart wird.
  • Ihr Programm ist mit AIO möglicherweise etwas einfacher, da Sie keinen zusätzlichen Thread benötigen, um einen Lese- oder Schreibaufruf zu blockieren.

Das heißt, posix AIO hat eine ziemlich umständliche Oberfläche, zum Beispiel:

  • Das einzige effiziente und gut unterstützte Mittel für Ereignisrückrufe sind Signale, was die Verwendung in einer Bibliothek erschwert, da Signalnummern aus dem prozessglobalen Signal-Namespace verwendet werden. Wenn Ihr Betriebssystem keine Echtzeitsignale unterstützt, bedeutet dies auch, dass Sie alle ausstehenden Anforderungen durchlaufen müssen, um herauszufinden, welche tatsächlich abgeschlossen wurden (dies ist beispielsweise bei Mac OS X der Fall, nicht bei Linux). Das Abfangen von Signalen in einer Umgebung mit mehreren Threads führt auch zu einigen kniffligen Einschränkungen. Sie können normalerweise nicht auf das Ereignis im Signalhandler reagieren, müssen jedoch ein Signal auslösen, in eine Pipe schreiben oder signalfd () (unter Linux) verwenden.
  • lio_suspend () hat die gleichen Probleme wie select (), es lässt sich nicht sehr gut mit der Anzahl der Jobs skalieren.
  • lio_listio () hat, wie implementiert, eine relativ begrenzte Anzahl von Jobs, die Sie übergeben können, und es ist nicht trivial, diese Grenze auf tragbare Weise zu finden. Sie müssen sysconf (_SC_AIO_LISTIO_MAX) aufrufen, was möglicherweise fehlschlägt. In diesem Fall können Sie die AIO_LISTIO_MAX-Definition verwenden, die nicht unbedingt definiert ist, aber dann können Sie 2 verwenden, die als garantiert unterstützt definiert ist.

Für reale Anwendungen mit posix AIO können Sie sich lighttpd (lighty) ansehen, das bei der Einführung der Unterstützung auch eine Leistungsmessung veröffentlicht hat.

Die meisten posix-Plattformen unterstützen derzeit posix AIO (Linux, BSD, Solaris, AIX, tru64). Windows unterstützt es über seine überlappenden Datei-E / A. Ich verstehe, dass nur Solaris, Windows und Linux Async wirklich unterstützen. Datei-E / A bis zum Treiber, während die anderen Betriebssysteme die asynchrone Funktion emulieren. E / A mit Kernel-Threads. Da Linux die Ausnahme darstellt, emuliert die Posix-AIO-Implementierung in glibc asynchrone Operationen mit Threads auf Benutzerebene, während die native asynchrone E / A-Schnittstelle (io_submit () usw.) bis zum Treiber wirklich asynchron ist, vorausgesetzt, der Treiber unterstützt sie .

Ich glaube, es ist unter Betriebssystemen ziemlich üblich, posix AIO für keine fd zu unterstützen, sondern es auf reguläre Dateien zu beschränken.

Arvid
quelle
Windows hat OVERLAPPED I / O-unterstützende Festplattendateien, seit Win32 zum ersten Mal veröffentlicht wurde. Es ist überhaupt nicht neu. Und unter POSIX ist der Signal-Namespace nicht prozess-global, sondern pro Thread. Signale werden an bestimmte Threads gesendet (oder ist aio eine Ausnahme davon, können Sie sich nicht sicher erinnern?).
Ben Voigt
1
Es gibt keine Möglichkeit anzugeben, an welchen Thread AIO seine Signale liefert. Unter Linux scheint es meistens an den Thread zu liefern, der den Befehl aio _ * () ausgegeben hat, aber nicht immer (die einzige Lösung, die ich dafür gefunden habe, war das Erstellen mehrerer Signaldateien). Vor einigen Jahren gab es einen Linux-Patch auf der Kernel-Mailingliste, der dies hinzufügte, aber es gelang ihm nie, und es wäre eine Erweiterung von POSIX gewesen. Unter Mac OS X scheinen die Signale (meiner Erfahrung nach) hauptsächlich an den Haupt-Thread gesendet zu werden. Ich glaube nicht, dass POSIX ein bestimmtes Verhalten erfordert. Wenn dies der Fall ist, würde ich gerne den Teil der Spezifikation sehen.
Arvid
5
Die Implementierung von aio_read / write von glibc verwendet Threads im Userland, sodass hier nicht einmal Kernel-Threads verwendet werden.
Marenz
Was bedeutet "immer typisch"? Schreibvorgänge werden vom Kernel für jede Methode zwischengespeichert, oder wenn AIO verwendet wird? Es scheint eine Möglichkeit zu geben, dass Software sicherstellt, dass der Schreibvorgang erfolgreich abgeschlossen wurde. Andernfalls können Integritäts- und Transaktionsziele nicht erreicht werden.
MikeB
Ein weiteres Live-Beispiel, in dem Sie AIO verwenden können, ist Nginx. Alle Modi werden unterstützt. Wenn Sie es vorziehen, in Userland-Threads zu verlagern, ist dies normalerweise viel schlimmer als direktes E / A, aber das native Linux-AIO ist dem direkten E / A ebenbürtig. Die Situation, in der AIO wesentlich vorteilhaft sein kann, ist ein starker Seiten-Cache-Druck. Begrifflicher Unterschied zwischen Async und Direct IOs kann hier gesehen werden ftp.dei.uc.pt/pub/linux/kernel/people/suparna/aio-linux.pdf
Docht
2

Es gibt aio_write - implementiert in glibc; Der erste Aufruf der Funktion aio_read oder aio_write erzeugt eine Reihe von Benutzermodus-Threads, aio_write- oder aio_read-Post-Anforderungen an diesen Thread. Der Thread führt pread / pwrite aus, und wenn er beendet ist, wird die Antwort an den blockierten aufrufenden Thread zurückgesendet.

Es gibt auch 'echtes' Aio - unterstützt von der Kernel-Ebene (brauche libaio dafür, siehe den io_submit-Aufruf http://linux.die.net/man/2/io_submit ); brauche dafür auch O_DIRECT (wird möglicherweise auch nicht von allen Dateisystemen unterstützt, aber die wichtigsten unterstützen es)

siehe hier:

http://lse.sourceforge.net/io/aio.html

http://linux.die.net/man/2/io_submit

Unterschied zwischen POSIX AIO und libaio unter Linux?

MichaelMoser
quelle
Viele der Mängel von aio_writesind oben in stackoverflow.com/a/5307557/13564
Glyph