2011-08-02 8 views
9

Testuję funkcje asynchroniczne io jądra (nie posix aio) i staram się dowiedzieć, jak to działa. Poniższy kod jest kompletnym programem, w którym po prostu wielokrotnie zapisuję tablicę do pliku otwartego przy pomocy O_DIRECT. Wystąpił błąd w funkcji zwrotnej "napisz brakujące bajty oczekuj 1024 dostał 0" (patrz instrukcja fprintf w work_done()).Funkcjonalność jądra linuksa

Dla tych, którzy nie znają AIO jądra, poniższy kod wykonuje następujące operacje:

  1. Init niektórych strukturach
  2. Przygotuj AIO (io_prep_pwrite)
  3. Prześlij żądania IO (io_submit)
  4. Sprawdzenie zakończenie zdarzenia (io_getevents)
  5. Wywołanie funkcji zwrotnej, aby sprawdzić, czy wszystko poszło dobrze.

Wystąpił błąd w kroku 5. Jeśli nie otworzę pliku za pomocą O_DIRECT, wszystko działa poprawnie, ale bije na cel, aby mieć asynchroniczne zapisywanie. Czy ktoś może mi powiedzieć, co robię źle? Czy to poprawne użycie jądra Aio, na przykład, czy moje odwołanie zwrotne jest poprawne? Czy są jakieś ograniczenia dotyczące korzystania z O_DIRECT?

skompilować za pomocą 'gcc -Wall test.c -laio'

Z góry dzięki.

/* 
* File: myaiocp.c 
* Author: kmehta 
* 
* Created on July 11, 2011, 12:50 PM 
* 
* 
* Testing kernel aio. 
* Program creates a 2D matrix and writes it multiple times to create a file of desired size. 
* Writes are performed using kernel aio functions (io_prep_pwrite, io_submit, etc.) 
*/ 
#define _GNU_SOURCE 
#define _XOPEN_SOURCE 600 

#include <stdio.h> 
#include <stdlib.h> 
#include <getopt.h> 
#include <pthread.h> 
#include <fcntl.h> 
#include <string.h> 
#include <sys/uio.h> 
#include <sys/time.h> 
#include <omp.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <errno.h> 
#include <libaio.h> 

char ** buf; 
long seg_size; 
int seg_rows; 
double total_size; 
char * filename; 
static int wait_count = 0; 

void io_task(); 
void cleanup(); 
void allocate_2D_matrix(int[]); 
int file_open(char *); 
void wr_done(io_context_t ctx, struct iocb* iocb, long res, long res2); 

int main(int argc, char **argv) { 
    total_size = 1048576;  //1MB 
    seg_size = 1024;   //1kB 
    seg_rows = 1024; 
    filename = "aio.out"; 

    int dims[] = {seg_rows, seg_size}; 
    allocate_2D_matrix(dims); //Creates 2D matrix 

    io_task(); 
    cleanup(); 

    return 0; 
} 

/* 
* Create a 2D matrix 
*/ 
void allocate_2D_matrix(int dims[2]) { 
    int i; 
    char *data; 

    //create the matrix 
    data = (char *) calloc(1, dims[0] * dims[1] * sizeof (char)); 
    if (data == NULL) { 
     printf("\nCould not allocate memory for matrix.\n"); 
     exit(1); 
    } 

    buf = (char **) malloc(dims[0] * sizeof (char *)); 
    if (buf == NULL) { 
     printf("\nCould not allocate memory for matrix.\n"); 
     exit(1); 
    } 

    for (i = 0; i < dims[0]; i++) { 
     buf[i] = &(data[i * dims[1]]); 
    } 
} 

static void io_error(const char *func, int rc) 
{ 
    if (rc == -ENOSYS) 
     fprintf(stderr, "AIO not in this kernel\n"); 
    else if (rc < 0) 
     fprintf(stderr, "%s: %s\n", func, strerror(-rc)); 
    else 
     fprintf(stderr, "%s: error %d\n", func, rc); 

    exit(1); 
} 

/* 
* Callback function 
*/ 
static void work_done(io_context_t ctx, struct iocb *iocb, long res, long res2) 
{ 

    if (res2 != 0) { 
     io_error("aio write", res2); 
     } 

     if (res != iocb->u.c.nbytes) { 
      fprintf(stderr, "write missed bytes expect %lu got %ld\n", 
        iocb->u.c.nbytes, res2); 
      exit(1); 
     } 
     wait_count --; 
     printf("%d ", wait_count); 
} 

/* 
* Wait routine. Get events and call the callback function work_done() 
*/ 
int io_wait_run(io_context_t ctx, long iter) 
{ 
     struct io_event events[iter]; 
     struct io_event *ep; 
     int ret, n; 

     /* 
     * get up to aio_maxio events at a time. 
     */ 
     ret = n = io_getevents(ctx, iter, iter, events, NULL); 
     printf("got %d events\n", n); 
     /* 
     * Call the callback functions for each event. 
     */ 
     for (ep = events ; n-- > 0 ; ep++) { 
      io_callback_t cb = (io_callback_t)ep->data ; struct iocb *iocb = ep->obj ; cb(ctx, iocb, ep->res, ep->res2); 
     } 
     return ret; 
} 

void io_task() { 
    long offset = 0; 
    int bufIndex = 0; 

    //Open file 
    int fd = file_open(filename); 

    //Initialize structures 
    long i; 
    long iter = total_size/seg_size; //No. of iterations to reach desired file size (total_size) 
    io_context_t myctx; 
    if(0 != io_queue_init(iter, &myctx)) 
    { 
     perror("Could not initialize io queue"); 
     exit(EXIT_FAILURE); 
    } 
    struct iocb * ioq[iter]; 

    //loop through iter times to reach desired file size 
    for (i = 0; i < iter; i++) { 
     struct iocb *io = (struct iocb*) malloc(sizeof (struct iocb)); 
     io_prep_pwrite(io, fd, buf[bufIndex], seg_size, offset); 
     io_set_callback(io, work_done); 
     ioq[i] = io; 

     offset += seg_size; 
     bufIndex ++; 
     if (bufIndex > seg_rows - 1) //If entire matrix written, start again from index 0 
      bufIndex = 0; 
    } 

    printf("done preparing. Now submitting..\n"); 
    if(iter != io_submit(myctx, iter, ioq)) 
    { 
     perror("Failure on submit"); 
     exit(EXIT_FAILURE); 
    } 

    printf("now awaiting completion..\n"); 
    wait_count = iter; 
    int res; 

    while (wait_count) { 
     res = io_wait_run(myctx, iter); 
     if (res < 0) 
      io_error("io_wait_run", res); 
    } 

    close(fd); 
} 

void cleanup() { 
    free(buf[0]); 
    free(buf); 
} 

int file_open(char *filename) { 
    int fd; 
    if (-1 == (fd = open(filename, O_DIRECT | O_CREAT | O_WRONLY | O_TRUNC, 0666))) { 
     printf("\nError opening file. \n"); 
     exit(-1); 
    } 

    return fd; 
} 

Odpowiedz

7

Przede wszystkim dobra praca z użyciem libaio zamiast POSIX aio.

Czy są jakieś ograniczenia dotyczące korzystania z O_DIRECT?

nie jestem w 100% pewien, że to jest prawdziwy problem, ale O_DIRECT ma pewne wymagania (podając głównie z TLPI):

  • Bufor przesyłane dane muszą być wyrównane w pamięci brzegowy, który jest wielokrotnością rozmiaru bloku (zastosować posix_memalign)
  • plik lub urządzenie przesunięcie, od którego rozpoczyna się transfer danych musi być wielokrotnością rozmiaru bloku
  • długość przesyłanych danych musi być wielokrotnością rozmiaru bloku:

W skrócie, widzę, że nie podejmujesz środków ostrożności, aby wyrównać pamięć w allocate_2D_matrix.

Jeśli nie otworzyć plik za pomocą O_DIRECT, wszystko działa w porządku, ale to bije celem posiadające asynchroniczny pisze.

Tak się nie dzieje. Asynchroniczne operacje we/wy działają dobrze bez numeru O_DIRECT (na przykład przy liczbie przyciętych połączeń systemowych).

+0

+1 dla odniesienia TLPI. To najgorsza książka w historii. – hari

+0

@cnicutar Świetnie. To naprawiło to. Jednak kilka pytań: 1.Dlaczego zaproponowałeś użycie libaio zamiast POSIX AIO? Nie mam dużego doświadczenia z Posix Aio, więc nie wiem. 2. Dlaczego mówisz, że asynchroniczne operacje we/wy działają dobrze bez O_DIRECT? Jak to redukuje nie. wywołań systemowych? W rzeczywistości uważam, że O_DIRECT jest bardziej efektywny, ponieważ pomija buforowanie jądra. – jitihsk

+1

'mmap' jest prawdopodobnie lepszym sposobem uzyskania pamięci wyrównanej do 4k niż' posix_memalign'. Ten ostatni na pewno będzie musiał zmarnować 4k na początku alokacji dla kilku bajtów księgowości, której potrzebuje, ponieważ przydziela on na poziomie szczegółowości strony (zakładając, że alokujesz wystarczającą ilość, że 'posix_memalign' obsługuje żądanie przez' mmap', a nie '. brk "). –