W odróżnieniu od operacji synchronicznej,
wykonanie operacji asynchronicznego wejściawyjścia (ang. Asynchronous
Input/Output, w skrócie AIO), z punktu widzenia zleceniodawcy,
składa się z dwóch faz:
1. zlecenia operacji,
2. pobrania informacji zwrotnej, że operacja
wykonała się.
Implementacja AIO odpowiada za wykonanie zleconego odczytu (zapisu) pomiędzy fazą pierwszą a drugą oraz za przekazanie informacji o jej zakończeniu. Zlecający proces może w międzyczasie wykonywać swoje zadania i odczyt (zapis) wykona się współbieżnie z jego ścieżką wykonania. Podstawowymi zaletami operacji asynchronicznych są:
AIO jest obecnie obsługiwane przez wszystkie liczące się systemy operacyjne. Operacje asynchroniczne zostały także dodane do jądra systemu Linux, począwszy od wersji 2.5.
Aby wykonywać operacje asynchroniczne w Linuksie należy
najpierw utworzyć dla nich kontekst
int io_setup(int maxevents, aio_context_t *ctxp)
Każda operacja wykonuje się w dokładnie jednym kontekście. W
tym samym kontekście może natomiast wykonywać się wiele operacji, odnoszących
się do dowolnych deskryptorów. Parametr maxevents określa ile co najwyżej
zdarzeń zakończenia ma móc przechowywać tworzony kontekst. Bufor do
przechowywania zdarzeń tworzony jest raz i ma stałą wielkość. Liczba zdarzeń ma
wpływ na liczbę wykonujących się asynchronicznie operacji. Zlecenie nowej
operacji powiedzie się tylko wtedy, gdy będzie zagwarantowane miejsce w buforze
na zdarzenie jej zakończenia, w przeciwnym wypadku przy próbie zlecenia
przekazany zostanie błąd [EAGAIN].
Po pomyślnym zakończeniu działania funkcji io setup
identyfikator nowo utworzonego kontekstu zostanie skopiowany pod adres ctxp.
By zlecić operację asynchroniczną, należy wywołać funkcję
int io_submit(aio_context_t ctx, long nr, struct iocb *iocbs[]).
Za jej pomocą zleca się wykonanie nr operacji. Wskaźniki do
ich rekordów kontrolnych znajdują się w tablicy iocbs. Funkcja przekazuje
liczbę operacji, które udało się zlecić (operacje z tablicy zlecane są kolejno).
W przypadku, gdy nie uda się zlecić żadnej operacji przekazywany jest kod błędu
dla pierwszej z nich.
Funkcja
int io_getevents(aio_context_t ctx, long min_nr, long nr, struct
io_event *events, struct timespec *timeout)
służy do oczekiwania na wykonanie się przynajmniej min nr
spośród operacji wykonujących się w kontekście ctx. W przypadku wykonania się przynajmniej
minimalnej liczby min nr operacji, na które oczekujemy lub po minięciu maksymalnego
czasu oczekiwania timeout funkcja przekazuje liczbę pobranych zdarzeń zakończenia oraz
wypełnia nimi tablicę events (każdej zakończonej operacji odpowiada dokładnie jedno zdarzenie).
Rekord zdarzenia ma postać:
struct
io_event {
__u64 data; /* wartość pola aio_data z rekordu kontrolnego */
__u64 obj; /* wskaźnik do rekordu kontrolnego struct iocb */
__s64 res; /* rezultat operacji */
__s64 res2; /* rezultat pomocniczy */
}
gdzie obj jest wskaźnikiem do rekordu kontrolnego zdarzenia, data wartością podaną przez użytkownika w momencie zlecania operacji (pole aiocb->data), a res wynikiem, jakim operacja się zakończyła. (W jądrze 2.6.1 pole res2 nie jest używane).
Rekord kontrolny operacji asynchronicznej w systemie Linux zawiera wszystkie wymagane przez POSIX pola.
struct iocb {
__u64 aio_data; /* do wykorzystania przez u»ytkownika */
__u32
PADDED(aio_key, aio_reserved1); /* identyfikator */
__u16 aio_lio_opcode; /* kod przy zlecaniu wielu operacji */
__s16
aio_reqprio; /* priorytet */
__u32 aio_fildes; /* deskryptor pliku */
__u64 aio_buf; /* bufor do odczytu-zapisu */
__u64 aio_nbytes; /* liczba bajtów do odczytu-zapisu */
__s64 aio_offset; /* pozycja w pliku,od której zaczynamy */
__u64 aio_reserved2; /* zarezerwowane, na wsk. sygnaªu */
__u64 aio_reserved3; /* zarezerwowane */
};
Do usuwania kontekstu służy funkcja
int io_destroy(aio_context_t ctx).
W momencie jej wykonania proces traci kontrolę nad kontekstem, a system
operacyjny podejmuje próbę odwołania wszystkich związanych z kontekstem
operacji, które jeszcze się nie zakończyły.
Źródła: