Jaki jest stan asynchronicznych operacji we / wy (AIO) POSIX?

93

W sieci są porozrzucane strony, które opisują obiekty POSIX AIO z różną ilością szczegółów. Żaden z nich nie jest strasznie świeży. Nie jest jasne, co dokładnie opisują. Na przykład „oficjalna” (?) Witryna internetowa dotycząca obsługi asynchronicznych operacji we / wy jądra Linuksa mówi, że gniazda nie działają, ale strony podręcznika „aio.h” na mojej stacji roboczej Ubuntu 8.04.1 wydają się sugerować, że działa dla dowolnych deskryptorów plików. Jest też inny projekt, który wydaje się działać w warstwie bibliotecznej z jeszcze mniejszą ilością dokumentacji.

Chciałbym wiedzieć:

  • Jaki jest cel POSIX AIO? Biorąc pod uwagę, że najbardziej oczywisty przykład implementacji, jaki mogę znaleźć, mówi, że nie obsługuje ona gniazd, całość wydaje mi się dziwna. Czy to tylko dla asynchronicznego dysku we / wy? Jeśli tak, dlaczego hiper-ogólny interfejs API? Jeśli nie, dlaczego operacje we / wy dysku są pierwszą zaatakowaną rzeczą?
  • Gdzie są przykłady kompletnych programów POSIX AIO, na które mogę spojrzeć?
  • Czy ktoś naprawdę go używa?
  • Jakie platformy obsługują POSIX AIO? Jakie części tego obsługują? Czy ktoś naprawdę popiera domniemane „Każde wejście / wyjście do dowolnego FD”, które <aio.h>wydaje się obiecać?

Inne dostępne mi mechanizmy multipleksowania są całkiem dobre, ale przypadkowe fragmenty informacji, które tam krążą, zaciekawiły mnie.

Glif
źródło

Odpowiedzi:

27

Sieciowe operacje we / wy nie są priorytetem dla AIO, ponieważ wszyscy piszący serwery sieciowe POSIX stosują nieblokujące podejście oparte na zdarzeniach. Podejście w starym stylu Java „miliardy blokujących wątków” jest okropnie do bani.

We / wy zapisu na dysku jest już buforowane, a operacje I / O odczytu z dysku można wstępnie ustawić w buforze za pomocą funkcji takich jak posix_fadvise. To pozostawia bezpośrednie, niebuforowane dyskowe operacje we / wy jako jedyny użyteczny cel AIO.

Bezpośrednie, niebuforowane operacje we / wy są naprawdę przydatne tylko w transakcyjnych bazach danych, a te mają tendencję do pisania własnych wątków lub procesów w celu zarządzania dyskowymi operacjami we / wy.

Tak więc ostatecznie POSIX AIO pozostaje w pozycji nie służącej żadnemu użytecznemu celowi. Nie używaj tego.

Zan Lynx
źródło
8
A co z odczytem / zapisem z sieciowych systemów plików (NFS, Samba)?
Alex B
1
dobrze. Mam kilku wielkich, głupich pisarzy, którzy, jeśli pozwolę im przejść do pamięci podręcznej, osiągną wartość dirty_ratio na szczytach, blokując wszystkich innych. Jeśli po prostu użyję na nich bezpośredniego IO, będzie to zbyt wolne. Gdybym miał tylko 1 wątek, poradziłbym sobie samodzielnie, ale trudno będzie obsługiwać różne priorytety IO w 1 bieżniku. AIO + CFQ to naprawdę dobra kombinacja, gdyby AIO zadziałało
n-alexander
37
Nie zgadzam się. Dyskowe operacje we / wy są zwykle buforowane, ale mogą blokować. Podczas odpytywania () pliku FD zawsze raportuje, że FD jest czytelny, nawet jeśli będzie blokowany. Uniemożliwia to wykonywanie operacji nieblokujących na plikach dyskowych w sposób zdarzeniowy, chyba że używa się wątków lub AIO.
Hongli
2
@Matt: Porządek nie jest ważny dla gniazd datagramowych. @Zan: async I / O jest bardzo przydatne do wstępnego buforowania danych strumieniowych w czasie rzeczywistym, np. Odtwarzaczy multimedialnych.
Ben Voigt
13
Nie jest prawdą, że AIO jest bezużyteczne w systemach opartych na zdarzeniach. W rzeczywistości możesz uzyskać zerową kopię sieciową z odpowiednim AIO, czego nie można uzyskać z powiadomieniem opartym na zdarzeniach do recv (). Inne rzeczy mogą sprawiać, że jest to głównie teoretyczne ograniczenie, ale myślę, że brak odpowiedniego AIO (a la OVERLAPPED w systemie Windows) jest jedną z ostatnich dużych dziur w Linuksie.
Jon Watte,
70

Wydajne wykonywanie operacji wejścia / wyjścia gniazda zostało rozwiązane za pomocą kqueue, epoll, portów zakończenia IO i podobnych. Wykonywanie asynchronicznych operacji wejścia / wyjścia pliku jest czymś w rodzaju późnego zjawiska (poza nakładającymi się operacjami wejścia / wyjścia systemu Windows i wczesnym wsparciem dla Posix AIO).

Jeśli szukasz wejścia / wyjścia gniazda, prawdopodobnie lepiej będzie, jeśli użyjesz jednego z powyższych mechanizmów.

Dlatego głównym celem AIO jest rozwiązanie problemu asynchronicznych operacji wejścia / wyjścia dysku. Najprawdopodobniej dlatego Mac OS X obsługuje AIO tylko dla zwykłych plików, a nie gniazd (ponieważ kqueue i tak robi to o wiele lepiej).

Operacje zapisu są zazwyczaj buforowane przez jądro i opróżniane w późniejszym czasie. Na przykład, gdy głowica odczytująca napędu przechodzi obok lokalizacji, w której ma zostać zapisany blok.

Jednak w przypadku operacji odczytu, jeśli chcesz, aby jądro ustalało priorytety i porządkowało odczyty, AIO jest naprawdę jedyną opcją. Oto dlaczego jądro może (teoretycznie) zrobić to lepiej niż jakakolwiek aplikacja na poziomie użytkownika:

  • Jądro widzi wszystkie dyskowe operacje we / wy, a nie tylko zadania dyskowe aplikacji, i może je porządkować na poziomie globalnym
  • Jądro (może) wiedzieć, gdzie jest głowica odczytu dysku i może wybrać zadania odczytu, które przekazujesz do niego w optymalnej kolejności, aby przesunąć głowicę na najmniejszą odległość
  • Jądro może korzystać z natywnego kolejkowania poleceń celu dalszej optymalizacji operacji odczytu
  • Możesz być w stanie wykonać więcej operacji odczytu na wywołanie systemowe używając lio_listio () niż z readv (), szczególnie jeśli twoje odczyty nie są (logicznie) ciągłe, oszczędzając odrobinę narzutu wywołań systemowych.
  • Twój program może być nieco prostszy z AIO, ponieważ nie potrzebujesz dodatkowego wątku do blokowania w wywołaniu odczytu lub zapisu.

To powiedziawszy, posix AIO ma dość niezręczny interfejs, na przykład:

  • Jedynym wydajnym i dobrze obsługiwanym sposobem wywołań zwrotnych zdarzeń są sygnały, co utrudnia użycie go w bibliotece, ponieważ oznacza użycie numerów sygnałów z globalnej przestrzeni nazw sygnałów. Jeśli Twój system operacyjny nie obsługuje sygnałów czasu rzeczywistego, oznacza to również, że musisz przejrzeć wszystkie oczekujące żądania, aby dowiedzieć się, które z nich zostało ukończone (dotyczy to na przykład systemu Mac OS X, a nie Linuksa). Przechwytywanie sygnałów w środowisku wielowątkowym również stwarza pewne trudne ograniczenia. Zwykle nie możesz zareagować na zdarzenie wewnątrz obsługi sygnału, ale musisz podnieść sygnał, napisać do potoku lub użyć signalfd () (w systemie Linux).
  • lio_suspend () ma te same problemy co select (), nie skaluje się dobrze z liczbą zadań.
  • lio_listio (), jak zaimplementowano, ma dość ograniczoną liczbę zadań, które można przekazać, i nie jest trywialne znalezienie tego ograniczenia w przenośny sposób. Musisz wywołać sysconf (_SC_AIO_LISTIO_MAX), co może się nie powieść, w takim przypadku możesz użyć definicji AIO_LISTIO_MAX, która niekoniecznie jest zdefiniowana, ale możesz użyć 2, które jest zdefiniowane jako gwarantowane do obsługi.

Jeśli chodzi o rzeczywistą aplikację korzystającą z posix AIO, możesz rzucić okiem na lighttpd (lighty), który również opublikował pomiar wydajności podczas wprowadzania wsparcia.

Większość platform posix obsługuje już posix AIO (Linux, BSD, Solaris, AIX, tru64). Windows obsługuje to poprzez nakładające się pliki I / O. Rozumiem, że tylko Solaris, Windows i Linux naprawdę obsługują async. plik I / O aż do sterownika, podczas gdy inne systemy operacyjne emulują asynchroniczne. I / O z wątkami jądra. Linux jest wyjątkiem, jego implementacja Posix AIO w glibc emuluje operacje asynchroniczne z wątkami na poziomie użytkownika, podczas gdy jego natywny asynchroniczny interfejs I / O (io_submit () itp.) Jest naprawdę asynchroniczny aż do sterownika, zakładając, że sterownik go obsługuje .

Uważam, że dość powszechne wśród systemów operacyjnych jest nieobsługiwanie POSIX AIO dla żadnego fd, ale ograniczenie tego do zwykłych plików.

Arvid
źródło
Windows miał OVERLAPPED I / O obsługujące pliki dyskowe od czasu pojawienia się Win32. To wcale nie jest nowe. A w POSIX przestrzeń nazw sygnału nie jest globalna dla procesu, lecz dla wątku. Sygnały są dostarczane do poszczególnych wątków (czy może aio jest wyjątkiem, nie pamiętam na pewno?).
Ben Voigt
1
Nie ma sposobu, aby określić, do którego wątku AIO dostarcza swoje sygnały. W Linuksie wydaje się, że dostarcza go głównie do wątku, który wydał polecenie aio _ * (), ale nie zawsze (jedynym rozwiązaniem, które znalazłem, było utworzenie wielu sygnałów sygnalizacyjnych). Kilka lat temu na liście mailingowej jądra pojawiła się łatka o Linuksie, która by to dodała, ale nigdy się nie pojawiła i byłaby to rozszerzenie POSIX. W systemie Mac OS X wydaje się, że sygnały są głównie dostarczane do głównego wątku (z mojego doświadczenia). Nie sądzę, aby POSIX wymagał określonego zachowania, a jeśli tak, chciałbym zobaczyć część specyfikacji.
Arvid
5
Implementacja aio_read / write w glibc używa wątków w przestrzeni użytkownika, więc nawet wątki jądra nie są tutaj używane.
Marenz,
Co oznacza „zawsze typowo”? Zapisy są buforowane przez jądro dla dowolnej metody lub podczas korzystania z AIO? Wygląda na to, że musi istnieć sposób, aby oprogramowanie miało pewność, że zapis został pomyślnie zakończony; w przeciwnym razie nie można osiągnąć celów związanych z uczciwością i transakcjami.
MikeB,
Innym przykładem na żywo, w którym można używać AIO, jest nginx. Obsługiwane są wszystkie tryby. Jeśli wolisz odciążanie wątków użytkownika, normalnie byłoby to o wiele gorsze niż bezpośrednie IO, ale natywny AIO Linuksa jest na równi z bezpośrednim IO. Sytuacja, w której AIO może przynosić znaczne korzyści, to poważne obciążenie pamięci podręcznej stron. Koncepcyjną różnicę między Async i Direct IO można zobaczyć tutaj ftp.dei.uc.pt/pub/linux/kernel/people/suparna/aio-linux.pdf
wick
2

Istnieje aio_write - zaimplementowany w glibc; pierwsze wywołanie funkcji aio_read lub aio_write generuje liczbę wątków trybu użytkownika, żądań postów aio_write lub aio_read do tego wątku, wątek wykonuje pread / pwrite, a po zakończeniu odpowiedź jest wysyłana z powrotem do zablokowanego wątku wywołującego.

Ther jest również 'prawdziwy' Aio - obsługiwany przez poziom jądra (do tego potrzebny jest libaio, zobacz wywołanie io_submit http://linux.die.net/man/2/io_submit ); do tego potrzebny jest również O_DIRECT (również może nie być obsługiwany przez wszystkie systemy plików, ale główne go obsługują)

Spójrz tutaj:

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

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

Różnica między POSIX AIO i libaio w systemie Linux?

MichaelMoser
źródło
Wiele niedociągnięć aio_writeomówiono powyżej, na stackoverflow.com/a/5307557/13564
Glyph