2011-06-27 14 views
5

Implemento un servidor de juegos donde necesito leer y escribir. Así que acepto la conexión entrante y empiezo a leer usando aio_read() pero cuando necesito enviar algo, dejo de leer usando aio_cancel() y luego uso aio_write(). Dentro de la devolución de llamada de escritura, reanudo la lectura. Por lo tanto, leo todo el tiempo pero cuando necesito enviar algo - I pausa lectura.C - Cómo usar aio_read() y aio_write()

Funciona para ~ 20% del tiempo - en el caso de otra llamada a aio_cancel() falla con la "Operación ahora en progreso" - y no puedo cancelarlo (incluso dentro del ciclo permanente mientras ). Entonces, mi operación de escritura agregada nunca ocurre.

¿Cómo utilizar bien estas funciones? ¿Qué extrañé?

EDIT: Utilizado en Linux 2.6.35. Ubuntu 10 - 32 bit.

código Ejemplo:

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

Debe especificar qué sistema operativo es este. También intente crear un ejemplo autónomo mínimo que muestre el problema. De lo contrario, ¿estás seguro de que necesitas cancelarlo? Creo que está perfectamente bien tener pendientes las operaciones de lectura y escritura en el mismo descriptor de archivo. –

+0

En C++ utilizo dos hilos para operar en el mismo socket al mismo tiempo. Un lector, un escritor Creo que esto podría funcionar también con aio, pero no estoy seguro, pero tal vez esto podría responder a alguien. –

+0

¿Podría explicar por qué necesita detener la lectura? ¿Aio no le permite emitir una solicitud de escritura sin cancelar la lectura? –

Respuesta

3

Si desea tener colas AIO separadas para lecturas y escrituras, para que una escritura emitida posteriormente pueda ejecutarse antes de una lectura emitida anteriormente, puede usar dup() para crear un duplicado del socket y usar uno para emitir lecturas y el otro para emitir escrituras.

Sin embargo, secundo las recomendaciones para evitar AIO por completo y simplemente uso un epoll()-loop de eventos dirigidos con sockets no bloqueantes. Se ha demostrado que esta técnica escala con un gran número de clientes: si obtiene un uso elevado de la CPU, perfile y descubra dónde está sucediendo eso, porque es probable que no sea su bucle de evento sea el culpable.

+0

Utilicé 'dup()' y funcionó. Gracias. Entonces, ahora tengo ambas implementaciones: las probaré en vivo y compararé (la parte de la red se implementa como un módulo independiente que se puede cambiar en tiempo de compilación). – Slav

4

En primer lugar, consideren la posibilidad de vertido AIO. Hay muchas otras maneras de hacer E/S asincrónicas que no son tan braindead (sí, aio es breaindead). Muchas alternativas; si estás en Linux puedes usar libaio (io_submit y amigos). aio(7) menciona esto.

Volver a tu pregunta.
No he usado aio en mucho tiempo, pero esto es lo que recuerdo. aio_read y aio_write ambos pusieron solicitudes (aiocb) en alguna cola. Vuelven inmediatamente, incluso si las solicitudes se completarán algún tiempo después. Es muy posible poner en cola varias solicitudes sin preocuparse de lo que sucedió con las anteriores. Por lo tanto, en pocas palabras: deje de cancelar las solicitudes de lectura y continúe añadiéndolas.

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

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

Más tarde usted es libre de usar esperar aio_suspend, sondeo utilizando aio_error, esperar a que las señales etc.

que veo que mencionas epoll en su comentario. Definitivamente debe ir por libaio.

+0

Gracias. ¿Qué se trata de la cadena "leer-escribir-leer-escribir"? ¿Podré, por ejemplo, escribir dos veces sin leer entre ellas?Actualmente tengo una cola real: no puedo escribir sin esperar a read_handler (DOY aio_write(), pero no hace nada hasta que aio_read() haga su trabajo: lea al menos un byte del cliente). Veré ** libaio **. – Slav

+0

http://lse.sourceforge.net/io/aio.html - ¿Esto es sobre libaio? Si es así, tiene la cláusula ** "AIO de lectura y escritura en los sockets" ** en el menú "¿Qué no funciona?" **. ¿Me perdí algo? – Slav

+0

¿Puedes darme un ling? – Slav

1

No debería haber ninguna razón para detener o cancelar una solicitud de lectura o escritura aio solo porque necesita hacer otra lectura o escritura. Si ese fuera el caso, eso vencería el punto de lectura y escritura asíncrona, ya que su propósito principal es permitirle configurar una operación de lectura o escritura, y luego seguir adelante. Dado que se pueden poner en cola varias solicitudes, sería mucho mejor configurar un par de agrupaciones de lectura/escritura asíncronas donde pueda tomar un conjunto de estructuras preinicializadas aiocb de un grupo "disponible" que se haya configurado para operaciones asíncronas siempre que lo necesite ellos, y luego devolverlos a otro grupo "terminado" cuando hayan terminado y usted puede acceder a los buffers que señalan. Mientras están en el medio de una lectura o escritura asíncrona, estarían en un grupo "ocupado" y no serían tocados. De esta forma, no tendrá que seguir creando estructuras aiocb dinámicamente en el montón cada vez que necesite realizar una operación de lectura o escritura, aunque está bien que lo haga ... no es muy eficiente si no planea pasar por un cierto límite, o planee tener solo un cierto número de solicitudes "en vuelo".

BTW, tenga en cuenta con un par de diferentes solicitudes asíncronas en vuelo que su controlador de lectura/escritura asíncrono puede ser interrumpido por otro evento de lectura/escritura. Entonces realmente no quieres estar haciendo mucho con tu manejador. En el escenario anterior que describí, su manejador básicamente movería la estructura aiocb que desencadenó el manejador de señal de uno de los grupos a los siguientes en las etapas listadas "disponible" -> "ocupado" -> "finalizado". Su código principal, después de leer desde el búfer al que apuntan las estructuras aiocb en el conjunto "terminado", movería la estructura al grupo "disponible".

0

A menos que no me equivoque, POSIX AIO (es decir, aio_read(), aio_write() y así sucesivamente) se garantiza que solo funciona en los descriptores de archivos buscables. Desde el aio_read() página de manual:

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. 

Para los productos que no tienen una posición de archivo asociado, como conectores de red, AFAICS, POSIX AIO es indefinido. Tal vez funcione en su configuración actual, pero parece más por accidente que por diseño.

Además, en Linux, POSIX AIO se implementa en glibc con la ayuda de subprocesos de espacio de usuario.

Es decir, donde sea posible, use IO no bloqueante y epoll(). Sin embargo, epoll() no funciona para los descriptores de archivos buscables, como archivos regulares (lo mismo vale para el clásico select()/poll() también); en ese caso, POSIX AIO es una alternativa para rodar su propio grupo de subprocesos.