Quel est l'état des E / S asynchrones (AIO) POSIX?

93

Il existe des pages dispersées sur le Web qui décrivent les installations POSIX AIO avec plus ou moins de détails. Aucun d'entre eux n'est terriblement récent. On ne sait pas exactement ce qu'ils décrivent. Par exemple, le site Web "officiel" (?) Pour la prise en charge des E / S asynchrones du noyau Linux indique ici que les sockets ne fonctionnent pas, mais les pages de manuel "aio.h" sur ma station de travail Ubuntu 8.04.1 semblent toutes impliquer que cela fonctionne pour les descripteurs de fichiers arbitraires. Ensuite, il y a un autre projet qui semble fonctionner au niveau de la couche bibliothèque avec encore moins de documentation.

J'aimerais savoir:

  • À quoi sert POSIX AIO? Étant donné que l'exemple le plus évident d'implémentation que je puisse trouver dit qu'il ne prend pas en charge les sockets, tout cela me semble bizarre. Est-ce uniquement pour les E / S de disque asynchrones? Si oui, pourquoi l'API hyper-générale? Sinon, pourquoi les E / S disque sont-elles la première chose qui a été attaquée?
  • Où puis-je consulter des exemples de programmes POSIX AIO complets ?
  • Quelqu'un l'utilise-t-il réellement, pour de vrai?
  • Quelles plates-formes prennent en charge POSIX AIO? Quelles parties soutiennent-ils? Quelqu'un prend-il vraiment en charge le "Any I / O to any FD" implicite qui <aio.h>semble promettre?

Les autres mécanismes de multiplexage dont je dispose sont parfaitement bons, mais les fragments aléatoires d'informations qui flottent là-bas m'ont rendu curieux.

Glyphe
la source

Réponses:

27

Les E / S réseau ne sont pas une priorité pour AIO car tous ceux qui écrivent des serveurs réseau POSIX utilisent une approche non bloquante basée sur les événements. L'approche Java à l'ancienne "milliards de threads de blocage" est horriblement nul.

Les E / S d'écriture sur disque sont déjà mises en mémoire tampon et les E / S de lecture de disque peuvent être préchargées dans la mémoire tampon à l'aide de fonctions telles que posix_fadvise. Cela laisse les E / S de disque directes et non tamponnées comme le seul objectif utile pour AIO.

Les E / S directes et non tamponnées ne sont vraiment utiles que pour les bases de données transactionnelles, et celles-ci ont tendance à écrire leurs propres threads ou processus pour gérer leurs E / S disque.

Donc, à la fin, cela laisse POSIX AIO dans la position de ne servir aucun but utile. Ne l'utilisez pas.

Zan Lynx
la source
8
Qu'en est-il de la lecture / écriture à partir de systèmes de fichiers réseau (NFS, Samba)?
Alex B
1
bien. J'ai plusieurs gros écrivains stupides qui, si je les laisse aller en cache, atteindront dirty_ratio aux pics, bloquant tout le monde. Si j'utilise juste IO direct sur eux, c'est beaucoup trop lent. Si je n'avais qu'un seul fil, je pourrais gérer moi-même, mais il sera difficile de prendre en charge différentes priorités d'E / S en une seule marche. AIO + CFQ semblerait vraiment une bonne combinaison, si AIO fonctionnait
n-alexander
37
Je ne suis pas d'accord. Les E / S disque ont tendance à être mises en mémoire tampon, mais elles peuvent être bloquantes. Lors de l'interrogation d'un fichier FD, il signale toujours que le FD est lisible, même s'il se bloque. Cela rend impossible d'effectuer des opérations non bloquantes sur les fichiers du disque de manière événementielle, sauf si l'on utilise des threads ou AIO.
Hongli
2
@Matt: L'ordre n'est pas important pour les sockets de datagramme. @Zan: les E / S asynchrones sont très utiles pour le pré-tampon des données de streaming en temps réel, par exemple les lecteurs multimédias.
Ben Voigt
13
Il n'est pas vrai que AIO est inutile dans les systèmes basés sur les événements. Vous pouvez réellement accéder à un réseau sans copie avec un AIO approprié, ce que vous ne pouvez pas avec la notification basée sur les événements à recv (). D'autres choses peuvent conspirer pour en faire principalement une limitation théorique, mais je pense que le manque d'AIO approprié (à la OVERLAPPED sous Windows) est l'un des derniers grands trous de Linux.
Jon Watte
70

Effectuer efficacement les E / S de socket a été résolu avec kqueue, epoll, les ports d'achèvement d'E / S et autres. Faire des E / S de fichiers asynchrones est une sorte de retard (à part les E / S superposées de Windows et le support précoce de Solaris pour posix AIO).

Si vous recherchez des E / S de socket, vous feriez probablement mieux d'utiliser l'un des mécanismes ci-dessus.

L'objectif principal d'AIO est donc de résoudre le problème des E / S de disque asynchrones. C'est très probablement pourquoi Mac OS X ne prend en charge AIO que pour les fichiers normaux, et non les sockets (puisque kqueue le fait tellement mieux de toute façon).

Les opérations d'écriture sont généralement mises en cache par le noyau et supprimées ultérieurement. Par exemple, lorsque la tête de lecture du lecteur passe par l'emplacement où le bloc doit être écrit.

Cependant, pour les opérations de lecture, si vous voulez que le noyau priorise et ordonne vos lectures, AIO est vraiment la seule option. Voici pourquoi le noyau peut (théoriquement) faire cela mieux que n'importe quelle application de niveau utilisateur:

  • Le noyau voit toutes les E / S disque, pas seulement les tâches de disque de vos applications, et peut les ordonner au niveau global
  • Le noyau sait (peut) où se trouve la tête de lecture du disque et peut choisir les tâches de lecture que vous lui transmettez dans un ordre optimal, pour déplacer la tête sur la distance la plus courte.
  • Le noyau peut tirer parti de la mise en file d'attente des commandes native en d' pour optimiser davantage vos opérations de lecture
  • Vous pourrez peut-être effectuer plus d'opérations de lecture par appel système en utilisant lio_listio () qu'avec readv (), surtout si vos lectures ne sont pas (logiquement) contiguës, économisant un tout petit peu de surcharge d'appel système.
  • Votre programme peut être légèrement plus simple avec AIO car vous n'avez pas besoin d'un thread supplémentaire pour bloquer un appel en lecture ou en écriture.

Cela dit, posix AIO a une interface assez maladroite, par exemple:

  • Le seul moyen efficace et bien pris en charge des rappels d'événements est via des signaux, ce qui le rend difficile à utiliser dans une bibliothèque, car cela signifie utiliser des numéros de signaux de l'espace de noms de signaux global du processus. Si votre système d'exploitation ne prend pas en charge les signaux en temps réel, cela signifie également que vous devez parcourir toutes vos requêtes en suspens pour déterminer laquelle est réellement terminée (c'est le cas pour Mac OS X par exemple, pas pour Linux). La capture de signaux dans un environnement multi-thread entraîne également des restrictions délicates. Vous ne pouvez généralement pas réagir à l'événement à l'intérieur du gestionnaire de signaux, mais vous devez augmenter un signal, écrire dans un tube ou utiliser signalfd () (sous Linux).
  • lio_suspend () a les mêmes problèmes que select (), il ne s'adapte pas très bien au nombre de jobs.
  • lio_listio (), tel qu'implémenté, a un nombre assez limité de tâches que vous pouvez transmettre, et ce n'est pas anodin de trouver cette limite de manière portable. Vous devez appeler sysconf (_SC_AIO_LISTIO_MAX), ce qui peut échouer, auquel cas vous pouvez utiliser la définition AIO_LISTIO_MAX, qui n'est pas nécessairement définie, mais vous pouvez alors utiliser 2, qui est défini comme étant garanti d'être pris en charge.

En ce qui concerne les applications réelles utilisant posix AIO, vous pouvez jeter un œil à lighttpd (lighty), qui a également publié une mesure des performances lors de l'introduction du support.

La plupart des plates-formes posix prennent désormais en charge posix AIO (Linux, BSD, Solaris, AIX, tru64). Windows le prend en charge via ses E / S de fichiers superposées. Je crois comprendre que seuls Solaris, Windows et Linux prennent vraiment en charge l'async. E / S de fichier jusqu'au pilote, tandis que les autres systèmes d'exploitation émulent l'async. E / S avec les threads du noyau. Linux étant l'exception, son implémentation AIO posix dans la glibc émule les opérations asynchrones avec des threads de niveau utilisateur, alors que son interface d'E / S asynchrone native (io_submit () etc.) est vraiment asynchrone jusqu'au pilote, en supposant que le pilote le supporte .

Je pense qu'il est assez courant parmi les systèmes d'exploitation de ne pas prendre en charge posix AIO pour aucun fd, mais de le limiter aux fichiers normaux.

Arvid
la source
Windows a des fichiers de disque prenant en charge les E / S OVERLAPPED depuis la sortie de Win32. Ce n'est pas du tout nouveau. Et sur POSIX, l'espace de noms du signal n'est pas global au processus, il est par thread. Les signaux sont livrés à des threads particuliers (ou est-ce que aio fait exception à cela, je ne m'en souviens pas avec certitude?).
Ben Voigt
1
Il n'y a aucun moyen de spécifier à quel thread AIO délivre ses signaux. Sur Linux, il semble le livrer principalement au thread qui a émis la commande aio _ * (), mais pas toujours (la seule solution que j'ai trouvée à cela était de créer plusieurs signalfds). Il y a quelques années, il y avait un correctif Linux sur la liste de diffusion du noyau qui ajouterait cela, mais il ne l'a jamais fait, et cela aurait été une extension de POSIX. Sur Mac OS X, les signaux semblent principalement être transmis au thread principal (d'après mon expérience). Je ne pense pas que POSIX nécessite un comportement spécifique, si c'est le cas, j'aimerais voir la partie de la spécification.
Arvid
5
L'implémentation de la glibc de aio_read / write utilise des threads dans le userland, donc même pas les threads du noyau sont utilisés ici.
Marenz
Que signifie «toujours typiquement»? Les écritures sont mises en cache par le noyau pour n'importe quelle méthode, ou lors de l'utilisation d'AIO? On dirait qu'il doit y avoir un moyen pour que le logiciel soit certain que l'écriture a été effectuée avec succès; sinon, l'intégrité et les objectifs transactionnels ne peuvent pas être atteints.
MikeB
Un autre exemple en direct où vous pouvez utiliser AIO est nginx. Tous les modes sont pris en charge. Si vous préférez le déchargement vers des threads utilisateur, vous le trouveriez normalement bien pire que les E / S directes, mais l'AIO natif de Linux est à égalité avec les E / S directes. La situation dans laquelle AIO peut être considérablement bénéfique est une forte pression du cache de pages. La différence conceptuelle entre Async et Direct IOs peut être vue ici ftp.dei.uc.pt/pub/linux/kernel/people/suparna/aio-linux.pdf
wick
2

Il y a aio_write - implémenté dans la glibc; Le premier appel de la fonction aio_read ou aio_write engendre un certain nombre de threads en mode utilisateur, aio_write ou aio_read post demandes à ce thread, le thread pread / pwrite et quand il est terminé, la réponse est renvoyée au thread appelant bloqué.

Il y a aussi 'vrai' aio - supporté par le niveau du noyau (besoin de libaio pour cela, voir l'appel io_submit http://linux.die.net/man/2/io_submit ); ont également besoin de O_DIRECT pour cela (peut également ne pas être pris en charge par tous les systèmes de fichiers, mais les principaux le prennent en charge)

vois ici:

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

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

Différence entre POSIX AIO et libaio sous Linux?

MichaelMoser
la source
La plupart des lacunes de aio_writesont couvertes ci-dessus, dans stackoverflow.com/a/5307557/13564
Glyph