2011-06-27 8 views
5

Implemento il server di giochi in cui ho bisogno di leggere e scrivere. Quindi accetto la connessione in entrata e inizio a leggerlo usando aio_read() ma quando ho bisogno di inviare qualcosa, smetto di leggere usando aio_cancel() e poi usare aio_write(). All'interno della callback di write riprendo a leggere. Quindi, leggo tutto il tempo ma quando ho bisogno di inviare qualcosa - I pausa lettura.C - Come utilizzare entrambi aio_read() e aio_write()

Si lavora per ~ 20% di tempo - in altri casi chiamata a aio_cancel() fallisce con "Operazione in corso" - e non posso cancellarlo (anche all'interno permanente ciclo mentre). Quindi, la mia operazione di scrittura aggiunta non accade mai.

Come utilizzare bene queste funzioni? Cosa mi sono perso?

MODIFICA: Utilizzato sotto Linux 2.6.35. Ubuntu 10 - 32 bit.

codice Esempio:

void handle_read(union sigval sigev_value) { /* handle data or disconnection */ } 
void handle_write(union sigval sigev_value) { /* free writing buffer memory */ } 
void start() 
{ 
    const int acceptorSocket = socket(AF_INET, SOCK_STREAM, 0); 
    struct sockaddr_in addr; 
memset(&addr, 0, sizeof(struct sockaddr_in)); 
addr.sin_family = AF_INET; 
addr.sin_addr.s_addr = INADDR_ANY; 
addr.sin_port = htons(port); 
    bind(acceptorSocket, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)); 

    listen(acceptorSocket, SOMAXCONN); 

    struct sockaddr_in address; 
socklen_t addressLen = sizeof(struct sockaddr_in); 

    for(;;) 
    { 
     const int incomingSocket = accept(acceptorSocket, (struct sockaddr*)&address, &addressLen); 
     if(incomingSocket == -1) 
     { /* handle error ... */} 
     else 
     { 
       //say socket to append outcoming messages at writing: 
       const int currentFlags = fcntl(incomingSocket, F_GETFL, 0); 
       if(currentFlags < 0) { /* handle error ... */ } 
       if(fcntl(incomingSocket, F_SETFL, currentFlags | O_APPEND) == -1) { /* handle another error ... */ } 

       //start reading: 
       struct aiocb* readingAiocb = new struct aiocb; 
       memset(readingAiocb, 0, sizeof(struct aiocb)); 
       readingAiocb->aio_nbytes = MY_SOME_BUFFER_SIZE; 
       readingAiocb->aio_fildes = socketDesc; 
       readingAiocb->aio_buf = mySomeReadBuffer; 
       readingAiocb->aio_sigevent.sigev_notify = SIGEV_THREAD; 
       readingAiocb->aio_sigevent.sigev_value.sival_ptr = (void*)mySomeData; 
       readingAiocb->aio_sigevent.sigev_notify_function = handle_read; 
       if(aio_read(readingAiocb) != 0) { /* handle error ... */ } 
      } 
    } 
} 

//called at any time from server side: 
send(void* data, const size_t dataLength) 
{ 
    //... some thread-safety precautions not needed here ... 

    const int cancellingResult = aio_cancel(socketDesc, readingAiocb); 
    if(cancellingResult != AIO_CANCELED) 
    { 
     //this one happens ~80% of the time - embracing previous call to permanent while cycle does not help: 
     if(cancellingResult == AIO_NOTCANCELED) 
     { 
      puts(strerror(aio_return(readingAiocb))); // "Operation now in progress" 
      /* don't know what to do... */ 
     } 
    } 
    //otherwise it's okay to send: 
    else 
    { 
     aio_write(...); 
    } 
} 
+0

È necessario specificare quale sistema operativo è. Prova anche a creare un esempio di auto-contenuto minimo che mostri il problema. Altrimenti, sei sicuro di doverlo cancellare? Penso che sia perfettamente ok avere entrambe le operazioni di lettura e scrittura in sospeso sullo stesso descrittore di file. –

+0

In C++ uso due thread per operare sullo stesso socket allo stesso tempo. Un lettore, uno scrittore. Penso che questo potrebbe funzionare anche con aio, ma non sono sicuro, ma forse questo potrebbe rispondere a qualcuno. –

+0

Puoi spiegare _perché_ hai bisogno di mettere in pausa la lettura? Non ti permette di inviare una richiesta di scrittura senza cancellare la lettura? –

risposta

3

Se si desidera disporre di code AIO separate per le letture e le scritture, in modo che una scrittura emessa successivamente possa essere eseguita prima di una lettura emessa in precedenza, è possibile utilizzare dup() per creare un duplicato del socket e utilizzarne uno per leggere e l'altro per pubblicare le scritture.

Tuttavia, in secondo luogo le raccomandazioni per evitare completamente AIO e utilizzare semplicemente un ciclo di eventi epoll()-driven con socket non bloccanti. È stato dimostrato che questa tecnica è in grado di adattarsi a un numero elevato di client: se si riscontra un elevato utilizzo della CPU, analizzarlo e scoprire dove sta accadendo, perché è probabile che sia non il proprio loop di eventi che è il colpevole.

+0

Ho usato 'dup()' e ha funzionato. Grazie. Quindi, ora ho entrambe le implementazioni - le metterò alla prova sul progetto live e confrontarle (la parte della rete è implementata come modulo standalone che può essere cambiato in fase di compilazione). – Slav

4

Prima di tutto, considerano il dumping aio. Ci sono molti altri modi per fare l'I/O asincrono che non sono come braindead (sì, aio è Breathead). Molte alternative; se sei su linux puoi usare libaio (io_submit e amici). aio(7) menziona questo.

Torna alla tua domanda.
Non ho usato aio da molto tempo ma ecco quello che ricordo. aio_read e aio_write hanno entrambi richiesto richieste (aiocb) su qualche coda. Ritornano immediatamente anche se le richieste verranno completate qualche tempo dopo. È possibile mettere in coda più richieste senza preoccuparsi di ciò che è accaduto ai precedenti. Quindi, in poche parole: interrompi l'annullamento delle richieste di lettura e continua ad aggiungerle.

/* populate read_aiocb */ 
rc = aio_read(&read_aiocb); 

/* time passes ... */ 
/* populate write_aiocb */ 
rc = aio_write(&write_aiocb) 

Più tardi si è liberi di aspettare utilizzando aio_suspend, sondaggio utilizzando aio_error, attendere che i segnali ecc

vedo si parla epoll nel tuo commento. Si dovrebbe assolutamente andare per libaio.

+0

Grazie. Che cos'è la catena "lettura-scrittura-lettura-scrittura"? Potrò, ad esempio, scrivere due volte senza leggere tra di loro?Attualmente sto affrontando la vera coda - non posso scrivere senza aspettare read_handler (Faccio aio_write(), ma non fa nulla finché aio_read() non farà il suo lavoro - legga almeno un byte dal client). Guarderò ** libaio **. – Slav

+0

http://lse.sourceforge.net/io/aio.html - si tratta di libaio? In tal caso, contiene ** la clausola "AIO leggere e scrivere sui socket" ** all'interno ** del menu "Cosa non funziona?" **. Ho perso qualcosa? – Slav

+0

Puoi darmi una molestia? – Slav

1

Non ci dovrebbero essere motivi per interrompere o annullare una richiesta di lettura o scrittura aio solo perché è necessario effettuare un'altra lettura o scrittura. In tal caso, ciò vanificherebbe l'intero punto di lettura e scrittura asincrona dal momento che lo scopo principale è quello di consentire l'impostazione di un'operazione di lettura o scrittura, e quindi andare avanti. Dal momento che più richieste possono essere accodate, sarebbe molto meglio impostare un paio di pool di lettori/scrittori asincroni in cui è possibile prendere una serie di strutture pre-inizializzate aiocb da un pool "disponibile" che è stato configurato per operazioni asincrone ogni volta che è necessario loro, e poi li restituiscono in un altro pool "finito" quando hanno finito e puoi accedere ai buffer a cui puntano. Mentre sono nel mezzo di una lettura o scrittura asincrona, sarebbero in un pool "occupato" e non sarebbero toccati. In questo modo non dovrai continuare a creare dinamicamente le strutture aiocb nell'heap ogni volta che devi eseguire un'operazione di lettura o scrittura, anche se va bene così ... non è molto efficiente se non prevedi di andare oltre certo limite, o prevede di avere solo un certo numero di richieste "in-flight".

BTW, tenere presente con un paio di richieste asincrone in volo diverse che il gestore di lettura/scrittura asincrono può effettivamente essere interrotto da un altro evento di lettura/scrittura. Quindi davvero non vuoi fare un sacco con il tuo gestore. Nello scenario sopra descritto, il gestore sposterebbe sostanzialmente la struttura aiocb che ha attivato il gestore di segnale da uno dei pool al successivo nelle fasi "disponibile" -> "occupato" -> "finito" elencate. Il tuo codice principale, dopo aver letto dal buffer indicato dalle strutture aiocb nel pool "finito", sposterebbe la struttura nel pool "disponibile".

0

A meno che non mi sbagli, POSIX AIO (vale a dire, aio_read(), aio_write() e così via) è garantito che funzioni solo su descrittori di file ricercabili. Dalla aio_read() manpage:

The data is read starting at the absolute file offset aiocbp->aio_offset, regardless of the 
    current file position. After this request, the value of the current file position is unspeci‐ 
    fied. 

Per i dispositivi che non hanno una posizione file associato come prese di rete, AFAICS, POSIX AIO è indefinito. Forse capita di lavorare sulla configurazione attuale, ma sembra più per caso che per design.

Inoltre, su Linux, POSIX AIO è implementato in glibc con l'aiuto dei thread userspace.

Ovvero, dove possibile utilizzare IO non bloccante ed epoll(). Tuttavia, epoll() non funziona per i descrittori di file ricercabili come i file normali (lo stesso vale per la classica select()/poll()); in tal caso POSIX AIO è un'alternativa al rolling pool di thread.

Problemi correlati