Historias
Slashboxes
Comentarios
 

Login Barrapunto

Login

[ Crear nueva cuenta ]

mig21 (7781)

mig21
  reversethis-{moc.liamg} {ta} {pb12gim}
https://twitter.com/yapw

Hola, soy Miguel. Algo que pueda ser relevante aquí... Uhmm... Me gusta escribir en mi bitácora de BP [barrapunto.com] y en su clon en blogspot: Yet Another Programming Weblog [blogspot.com]
Me gustaría que Barrapunto fuese un sitio con más discusiones técnicas y trato de hacer lo que está en mi mano. De todos modos, también me gusta leer flames ;)

No creo que te interese, pero en Lecturas aleatorias [blogspot.com] dejo registro de los libros que voy leyendo...

Esta es toda mi información de usuario :)

Down Kill Up Publicidad

Bitácora de mig21 (7781)

Martes, 02 de Mayo 2006

Más sobre Eventos contra Threads

08:19h.
Tecnología

Si alguien ha seguido las entradas de mi bitácora sabrá que me interesan mucho las técnicas que usan los servidores para recoger las peticiones de modo que sean óptimos y escalables(*) Pues bien, en Lambda the Ultimate apuntaban a un artículo que tiene varias virtudes: lo que presenta es interesante, pero además hace un resumen conciso del asunto. Se trata de "A Language-Based Approach to Unifying Events and Threads" (pdf) en el que se muestran las dos diferentes aproximaciones al problema y se tratan de unificar usando Haskell y conceptos, que además introduce y explica, como monads (¿mónadas?) y Continuation-passing style. Estimulante (y posiblemente poco práctico :) )

(*)El tema lo he comentado algunas veces: Programando servidores escalables, Porque los eventos son una mala idea y más recientemente en Threads y más threads apuntaba a Why Threads Are A Bad Idea (for most purposes))

Este hilo ha sido archivado. No pueden publicarse nuevos comentarios.
Mostrar opciones Umbral:
Y recuerda: Los comentarios que siguen pertenecen a las personas que los han enviado. No somos responsables de los mismos.
  • fork()'s, threads y demas

    (Puntos:1, Interesante)
    por pobrecito hablador el Martes, 02 Mayo de 2006, 15:25h (#736598)
    Cuando empiezas a programar, siempre te enseñan que la primera manera de hacer un servidor es haciendo un fork justo despues del accept() o el select() (dependiendo del tipo de socket) para crear una copia del programa que se encargue de la comunicacion y dejar que el programa padre se encargue de recibir nuevas conexiones.

    Esto siempre da pie a que te expliquen que en un servidor de verdad ("de los buenos" suelen decir...) el coste de copiar todas las paginas de memoria del programa padre es demasiado alto, por lo que se prefiere una de dos alternativas: a) dejar que el programa padre lleve todas las conexiones complicando bastante el codigo, y b) creando un thread para cada conexion, ya que solo se copia unas cuantas paginas y hay menos sobrecarga.

    Normalmente la opcion a) se descarta no por eficiencia, sino porque a poco que el protocolo sea minimamente complicado, el codigo queda completamente ilegible entre if, switch, y demas estructuras necesaria para mantener un registro que indique en que estado se encuentran cada una de las conexiones. La opcion b) se escoge como la correcta cuando se trata de servidores de tamaño medio y te "enseñan" a utilizarla con la excusa de que "ya aprenderas tecnicas mas avanzadas mas adelante".

    En realidad la opcion de los fork seria la optima si no fuera por la cantidad de tiempo que hay que pasar copiando el codigo del programa padre, y por eso el uso de threads (se copian solo las paginas de memoria que contiene el codigo del thread pero se mantiene la informacion del proceso del padre -> menos carga para el procesador/memoria)

    Por eso hace tiempo, en los servidores grandes de verdad (apache 1.x usa este esquema, el apache2 no lo he tocado pero dudo de que cambien algo que funciona; creo recordar que bind tambien, aunque no pondria la mano en el fuego) se hace mezcla de las dos aproximaciones: cuando se ejecuta el programa servidor, se crean "N" forks antes de empezar a hacer nada y cada uno de ellos crea "n" threads listos para manejar conexiones (este "listos" significa que se hace la copia en memoria y se deja un puntero posicionado justo para empezar a manejar la conexion en la instruccion siguiente a poco que se escriba la IP de la maquina que realiza conexion). La carga para el SO es minima ya que el servidor no estaba ejecutandose y no hay conexiones que atender, y que tarde un par de segundos en arrancar el servidor no suele ser problema. E incluso en sistemas avanzados, puede tenerse una cache de la situacion inicial y despues cargarla directamente desde disco a memoria.

    La ventaja de este sistema es que cuando llega una conexion siempre hay N+n threads libres para atenderla inmediatamente, y se puede dejar la creacion de otro thread que lo sustituya a los tiempos con menos cargas (periodos de peticion/respuesta y demas). La eleccion del proceso que maneja una conexion determinada puede hacerse tan simple como un contador de 1 a N y atiende el fork() que le toque, hasta sistemas en los que se balancea la carga entre N maquinas (y esta es casi la unica ventaja de usar fork(), que como se copia el proceso entero, puede escalarse a maquinas con varios procesadores e incluso maquinas diferentes sin que el resultado final cambie para el usuario).