Asynchroniczne wejście-wyjście

1. Co daje asynchroniczne wejście-wyjście (Asynchronous Input/Output - AIO)?

AIO umożliwia aplikacji (nawet jednemu wątkowi aplikacji) nakładanie na siebie operacji wejścia-wyjścia oraz przetwarzania danych. Jest to możliwe dzięki udostępnieniu interfejsu (io_submit) do zgłaszania jednego lub większej ilości żądań I/O w jednym wywołaniu systemowym (bez czekania na zakończenie) oraz osobnego interfejsu (io_getevents) do zbierania zakończonych operacji I/O (związanych z podaną grupą kończącą). Podstawowe zalety operacji asynchronicznych to:

 

 

2. Gdzie może być wykorzystane AIO?

AIO jest przede wszystkim wykorzystywane przez aplikacje wykonujące wiele operacji wejścia-wyjścia:


3. Jak zrealizowane jest AIO w linuxie 2.6?

a) Stworzenie kontekstu.

Każda operacja AIO wykonuje się w jednym kontekście (nie wyklucza to tego, że w ramach jednego kontekstu może się wykonywać wiele operacji). Tak więc, aby wykonywać operacje asynchroniczne należy najpierw utworzyć kontekst. Konteksty AIO opisywane są przez rekordy z przestrzeni jądra (struct kioctx):

struct kioctx {
         atomic_t              users;
         int                   dead;    
         struct mm_struct      *mm;  /*rekord mm_struct procesu,który utworzył kontekst*/
         unsigned long         user_id;
         struct kioctx         *next;         /*następny kontekst na liście w mm_struct*/
         wait_queue_head_t     wait;   /*kolejka do oczekiwania na zakończenie operacji*/
         spinlock_t            ctx_lock;      /*blokada dla sekcji krytycznej kontekstu*/
         int                   reqs_active; 
         struct list_head      active_reqs;    /*lista aktualnie wykonywanych operacji */
         struct list_head      run_list;         /*lista wywłaszczonych operacji*/
         unsigned              max_reqs;
         struct aio_ring_info  ring_info;
         struct  work_struct   wq;

};

Lista wskaźników do wszystkich stworzonych przez proces kotekstów trzymana jest w polu ioctx_list rekordu mm_struct (konteksty nie są dziedziczone przez proces potomny). Do utworzenia kontekstu służy funkcja sytemowa:

long sys_io_setup(unsigned nr_events,

                  aio_context_t *ctxp)

nr_events - maksymalna ilość zdarzeń (informujących o zakończeniu operacji I/O), jaką może otrzymać kontekst; *ctxp - pod ten adres zostanie skopiowany  identyfikator nowo utworzonego kontekstu;

 

b) Zlecenie operacji AIO

Gdy jest już utworzony kontekst, aplikacja (wątek) może zlecić wykonanie operacji AIO, poprzez wykorzystanie funkcji systemowej:

long sys_io_submit(aio_context_t ctx_id,

                   long nr,

                   struct iocb__user **iocbpp)

ctx_id - identyfikator kontekstu; nr - ilość zlecanych operacji, **iocbpp - wskaźniki do rekordów kontrolnych operacji do wykonania;

 

c) Odebranie wyniku operacji AIO

Gdy zlecone zostały operacje AIO, aplikacja (wątek) może odebrać wyniki operacji (zdarzenia), a dokładniej spróbować odczytać czy ich wykonanie zostało zakończone - należy określić minimalną i maksymalną liczbę operacji, na których zakończenie czekamy (są one pobierane z kolejki ukończonych operacji dla kontekstu, którego identyfikator musimy podać), a ponadto określamy czas, jaki będziemy czekać na odpowiednią ilość zakończonych operacji (wartość NULL oznacza czekanie 'aż do skutku'). Wywołanie tej funkcji systemowej ma postać:

long sys_io_getevents (aio_contex_t ctx_id,

                      long min_nr,

                      long nr,

                      struct io_event *events,

                      struct timespec *timeout)

ctx_id - identyfikator kontekstu; min_nr - minialna liczba operacji, na wykonanie których oczekujemy; nr - maksymalna liczba operacji, na wykonanie których oczekujemy; *events - rekordy zdarzeń (wykonanych operacji); *timeout - czas jaki czekamy na odpowiednią ilość zakończonych operacji (zdarzeń).

Rekord zdarzenia wygląda następująco:

struct io_event {
           __u64          data;           /* wartość pola aio_data z rekordu kontrolnego iocb */
           __u64          obj;            /* wskaźnik do rekordu kontrolnego iocb */
           __s64          res;            /* rezultat wykonania operacji (zdarzenia) */
           __s64          res2;           /* rezultat pomocniczy */

};

Rekord kontrolny (zawarty w zdarzeniu) ma natomiast następującą postać:

struct iocb {
           __u64   aio_data;                     /* wartość podana w czasie zlecania operacji */
           __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; 
           __u64   aio_reserved3;
};


 

d) Odwołanie operacji

Jeśli po zleceniu wykonania operacji AIO, nastąpi konieczność anulowania jej, można tego spróbować, wykorzystując następującą funkcję (przy jej wywoływaniu należy podać wskaźnik do rekordu kontrolnego operacji):

long sys_io_cancel(aio_context_t ctx_id,

                   struct iocb *iocb,
                   struct io_event *result)


ctx_id -  identyfikator kontekstu; *iocb - wskażnik do rekordu kontrolnego operaci, której wykonanie chcemy anulować; *result - tu jest kopiowany rekord zdarzenia (nie jest on umieszczany w kolejce operacji ukończonych)

 

e) Usuwanie kontekstu

Aplikacja (wątek) może usunąć uprzednio utworzony kontekst. Po jej wykonaniu aplikacja (wątek) traci kontrolę nad kontekstem, a system podejmuje próbę odwołania wszystkich związane z kontekstem (a nie zakończonych) operacji. Tak wygląda wywołanie wykonującej to funkcji systemowej:

long sys_io_destroy(aio_context_t ctx)

ctx - identyfikator kontekstu;

 

4. Pliki

5. Linki

 

 

autor: Mateusz Konikowski