Saturday, March 25, 2006

Asynchronous I/O rocks.

I used to use poll() / select() to check if a filedescriptor
was ready for writing/reading, for reading i learnt something
new.. i didnt know nothing about AIO things, so i made some
tests with this asynchronous functions... aio_read , aio_write ,
aio_cancel , aio_suspend , etc.. and is very easy. aio in unix
uses pthreads to read() a FD and continue the execution
of the process (i mean aio functions return a value immediatly).
Its good to know this type of methods , at the beginning,
if i knew this thing two years ago something different would
happen in my life haha :p
im in a new project related to multiple sockets , i need to
code it and make it work in HP-UX and solaris so i need
to use strictly posix , and i didnt know nothing about AIO.
Another fun thing is that im 'touching' some features of 'hypercube'
webserver code from a good friend anakata
to run on sparc & hpux, anakata use this , thats why i wanted
to learn wtf was happening. maybe this is something i needed to
know, but i didnt.. so , maybe lot of you havent used this.

So here is an example i made to ALL OF YOU , commented,
and i hope i can start being a good programmer.
This examples reads the stdin, and it enters a while(1) { sleep(1); printf(); } loop
and when the buffer is read from stdin a signal is sent and
the program finishes. this can be useful for sockets , poll()
can handle multiple events like urgent packets etc..
but for a "standard" I/O async this is very useful.
remember im not a computologist, but i like lot this shit. hehe


Greetz

Source here

8 comments:

Daniel Palacio said...

QUICK NOTE:
Para compilar en linux.
#include aio.h no sys/aio.h
(por cierto no se puede usar >< pork
cree que es HTML code)

y gcc file.c -lrt

Beck muy bien, me parecio muy interesante ver como funcionan los AIO.
Sobre lo de buen programador.

-Codigo: muy bien indentado y muy bien comentado, de hecho el codigo se ve bien, y creeme yo no digo eso casi nunca.
Comparado a tus otros codigos, es legible, bastante legible.
Igual te voy a criticar un poco.

Critica trivial:
-checar si malloc retorna NULL
Bueno no queria decir esto:
memset (&ptr, 0x0, BUF_SIZE); /* we fill with zeroes the buffer we are going to allocate the read data */

ptr esta declarado como "global" o mejor dicho estatica, asi que puedes estar seguro de que esta inicializado con 0's totalmente, pero igual no esta mal llamar a memset, y de hecho yo lo dejaria ahi, si lo hiciste de aposta, excelente :).

Bueno ahora va la parte mamona, no aguante las ganas de intentar romper tu programa, y claro, un hermoso underflow.

ptr[strlen ((char *) ptr) - 1] = 0; /* We 'delete' the last byte to print pretty the next line (delete the 0xa) */

Hmm me pregunto que pasa si le escribimos un NULL a ptr directamente.
strlen((char *)ptr == 0 - 1 == -1 ?
ptr[-1] ???????

Veamos si lo podemos hacer tronar:
---snip peque~o script para generar
char no printable---

#!/bin/bash
FILE="char.txt"
if [ $# != 2 ]; then
echo "Usage: $0 number char"
exit -1
else
NUM=$1
i=$2
# for ((i=0; i<=127;i++));
# do
python -c "print chr($i)*$NUM" >> $FILE
# $PROG < $FILE >> $OUTPUT
# done
fi

daniel@home:~/c-code$ ./genchar.sh 1 0

---meto un 0x0 en un archivo char.txt---
(gdb) run < char.txt
Starting program: /home/daniel/c-code/a.out < char.txt
[Thread debugging using libthread_db enabled]
[New Thread -1210054976 (LWP 14118)]
[New Thread -1208640592 (LWP 14121)]
aio_read = 0
async read buf []

Program received signal SIGUSR1, User defined signal 1.
[Switching to Thread -1210054976 (LWP 14118)]
0xffffe410 in __kernel_vsyscall ()
(gdb) c
Continuing.

Breakpoint 1, finish () at beck2.c:60
60 int debug = strlen((char *)ptr) - 1;
(gdb) n
61 ptr[strlen ((char *) ptr) - 1] = 0; /* We 'delete' the last byte to print pretty the next line (delete the 0xa) */
(gdb) p debug
$1 = -1
(gdb)
$2 = -1
(gdb) c
Continuing.
[Thread -1208640592 (LWP 14121) exited]
SIGNAL Received.. buffer is []
AIO alldone
finishing

Program exited normally.

Bueno no logre hacerlo tronar, sino tan solo escribir un byte atras del heap, ahora mismo no se me ocurre algo que se pueda hacer con esto como en este programa especifico.
Bueno ojala te hay gustado el demo y estes mas atento a strlen(x) - X cuando estes accesando un array, esto no lo hago de mamon, ojala a mi la gente me mostrara mis errores para aprender mas, pero si no te gusta que lo haga, solo dilo.

De resto todo perfecto, felicitaciones en serio, de hecho de tanto mamarte la vida mira ya te acostumbraste a comenter xD.

-daniel

beck said...

chido... esta bien , solo que bueno ,
si de hecho lo del memset() antes ptr no era global...
pero cuando agregue lo del signal decidi hacerlo global para usar la funcion void finish();

if(t == NULL)

bueno, yo suelo verificarlo... pero esta vez no.. es un simple programa de prueba y malloc no falla mas que cuando brk o sbrk fallan , y eso pasa cuando ya no hay memoria de donde agarrar los rangos.. entonces no tengo pedos.

Si quieres tronar el strlen()

no necesitas tanta mamada.

con que ejecutes en el argumento un ""

eso es un apuntador a NULL no necesitarias hacer un script en python.

solo bastaria un
if(!strlen(argv[1]))
exit(printf("Len debe ser mayor a 1\n"));

hay veces que no se puede confiar en todas las funciones



es como si te dijera


int main(void) {
if(printf("hola mundo\n") != sizeof("hola mundo\n")) {
perror("printf");
exit(EXIT_FAILURE);
}
return 0;
}


Saludos espero te haya servido eso de aio

daniel palacio said...

Hm no entendi eso de el argumento "" ?
Hice el shell script porque en este caso, la unica manera que finish() es llamado es si realmente se ha escrito almenos 1 byte, y necesitaba escribirle un 0x0. Eso es lo chido de este programa, y como aio funciona, usarlo en un httpd seria chido pero vas a ver que no es necesario, pero usarlo en un servidor IRC estaria muy bien, asi evitas que alguien pida coneccion y no retorne nada y te quedes esperando hasta un timeout, esta madre retorna inmediatamente si no hay nada.

Quisiera que explicaras mas eso de "" aplicado a tu programa porque no entendi.

Otra pregunta, has leido algo que diga pros y contras de aio vs select ?
aio vs poll ? yo por el momento gusto mas de poll porque es creo la mas sencilla, pero tambien se que select hace multiplexing que es una bonita optimizacion, sobretodo en httpd's. Por eso me pregunto porque estas usando aio's en un httpd, cual es le ventaja ?

-daniel

beck said...

haha chida pregunta.

aio_write() la cual no expuse en el ejemplo la uso para regresar la respuesta a usuarios en el httpd , con un timeout dado por un signal , a veces el pedo era que no podias reescribir a el usuario de regreso despues de la peticion y obtenia un sigpipe , para leer las conexiones ando usando poll , pero mas a bajo nivel con 3 syscalls
__NR_epoll_create (254)
__NR_epoll_ctl (255)
__NR_epoll_wait (256)

asio esta chido para el nonblocking , aunque tambien te recomiendo que a los sockets les hagas

ioctl(sock,FIONBIO,flags);

con FIONBIO en sys/filio.h para ponerle non-blocking.

otra cosa para la que uso aio_write es para hacer tests en realtime , si quiero lanzalrle 100 conexiones a un socket para escribirle 10kb por ejemplo de datos , con esa se aprovecha 100% el performance de la maquina por los threads y aparte porque todo ya se encuentra en espera para ser escrito .. vaya ya esta 'ejecutado' solo esperando a que el bandwidth permita escribir en el socket
luego pego el codigo del webserver

beck said...

mira como trueno el programa.

tu pones un 0x0 en el archivo.. eso al fin y al cabo sigue siendo 1 byte , aunque strlen() solo lee hasta el 0x0 pero bueno yo hice lo siguiente. que es mucho mas sencillo (son 2 cosas)

Este , aunque explotarlo esta mas avanzado

[0][beck@riemann:/tmp]$ echo "" | ./aio
aio_read = -1
Error in aio
aio_read: Illegal seek
async read buf []
async read buf []
async read buf []
^C
[130][beck@riemann:/tmp]$

lo que sucede aqui esq me estoy 'seekeando'
en un lugar del filedescriptor que no existe

otro es el siguiente , con gdb ya lo cheque , hazle

"display ptr-1" y checale

2)

[0][beck@riemann:/tmp]$ touch ne
[1][beck@riemann:/tmp]$ rm -f newfile
[0][beck@riemann:/tmp]$ touch newfile
[0][beck@riemann:/tmp]$ ls -al newfile
-rw-r--r-- 1 beck wheel 0 Mar 26 10:26 newfile
[0][beck@riemann:/tmp]$ gcc aio.c -o aio -ggdb
[0][beck@riemann:/tmp]$ ./aio < newfile
User defined signal 1
[158][beck@riemann:/tmp]$

Esto hace segfault en el thread. solo que como sabras es un pedo debugear otros contextos de procesos. , el signal llega pero ya no ejecuta nada.

beck said...

pero bueno , creeme que si me lo propongo , hago el codigo mas seguro :p , solo que meteria como 40% mas de codigo de lo que hago generalmente , y como te digo para programas con este proposito.. pues no lo hago. en programas de prueba , las unicas veces que si lo hago seguro es cuando miprograma debe correr como setuid , o con raw sockets.

beck said...

ahh y estas de acuerdo que verificar tanta madre llega a ser tedioso para el lector del codigo, es incluir una infinidad de if's

hay una cosa que vi extrania viendo un codigo de SGI.
es "standard" hacer casting de (void) cuando usas funciones que regresan algo.

por ejemplo este codigo hacia cosas asi

int main() {
....
(void)printf("hola mundo\n");
....
return 0;
}

ya que printf es "int" pero segun esto no es correcto .

Saludos , ya puse muchos posts haha ,

daniel said...

Hehe si vi lo de lo thread, pero a huevo que es cabron debbugearlo, dios quiera que no me toque, pero lo mismo decia de debbugear malloc y bueno al final una acaba teniendo que hacerlo, aunque si es tediosisimo, aguna sugerencia para aprender a debuggear threads ?

Sobre lo de hacer 1000000 checks, si es tedioso y tambien obscura un poco el codigo, a veces yo hago wrappers etx, igual uno nunca va a ver todos los bugs, asi que lo mejor es linkear con alguna debug library y correrlo ahi, usar valgrind etx, y en ultimas pedir ayuda. Claro, supongo que este codigo lo hiciste en 10 mins para postear, por ende no esperaba que hubieras testeado, pero si lo lamentable es que es mas demorado testear que programar...igual tambien es verdad que para la version final por ejemplo de tu httpd si te va a tocar hacer todo eso, igual yo soy demasiado perfeccionista entonces acabo haciendolo siempre :( y me demoro mucho haciendo cosas.....
-daniel