sisoputnfrba / foro

Foro de consultas para el trabajo práctico
148 stars 7 forks source link

recv() y muchos hilos #3939

Closed MariaSolGaraventa closed 3 months ago

MariaSolGaraventa commented 3 months ago

Buenas buenas! Tengo una duda bastante importante de sends, recvs e hilos.

Supongamos que mi Kernel tiene muchos hilos: planificacion, manejo de recursos, creacion de procesos... y todos están bloqueados en recv(), esperando, por ejemplo, un send de la CPU (un ejemplo a fines explicativos, no necesariamente es así en el TP). ¿Cómo sabe el send, o cada hilo, quién tiene que recibir el paquete? ¿Lo recibe cualquiera (o sea, el que llegue primero)? ¿Por magia de C, no se mezclan los sends y recv? Si sí se mezclan, ¿cómo se podría resolver? Estamos debatiendo hace ya un rato y no damos con una respuesta clara, jaja.

Gracias!

iago64 commented 3 months ago

Buenas! Cómo va?

En principio, descartemos la magia que casualmente C es un lenguaje que no la maneja mucho que digamos. Ahora bien, hay una sola pregunta que tengo que hacerte, todos los recv() son sobre el mismo socket o son sobre sockets distintos?

Respuestas posibles:

Saludos.-

MariaSolGaraventa commented 3 months ago

Lamentablemente... Son todos sobre el mismo socket. ¿Hay alguna de solucionarlo? ¿Semáforos? ¿Algo? 😔

Una cosa que pensamos (espero que no se vaya de mambo 💀):

Estamos descendiendo a la locura lentamente.

LeandroCarbajales commented 3 months ago

La pregunta importante es, para qué están haciendo recv() del mismo socket en varios hilos concurrentemente? En vez de emparchar el problema, capaz conviene solucionarlo de raíz :)

Champa123 commented 3 months ago

Capaz pueden centralizar las operaciones del socket en un hilo y si necesitan esperar por alguna respuesta para continuar ejecutando pueden usar algun semaforo:

Hilo 1

enviar_mensaje();
sem_wait(sem_esperar_recepcion);
uso(miDato);

Hilo2 (socket)

case MI_MENSAJE:
miDato = recibir_paquete()
sem_post(sem_esperar_recepcion);

Nota, posiblemente miDato sea una variable global y deberian "mutexearla"

Champa123 commented 3 months ago

O simplemente terminar la ejecucion del hilo enviando el mensaje y a la hora de recibir el dato continuar haciendo lo que tenian que hacer desde el hilo del socket que recibe el dato. Menos semaforos y mutex, mayor peligro a bloquear el socket pero nada que un pthread_create no solucione (?

GabrielBorre commented 3 months ago

Buenas Lean! Soy Gabriel y estoy en el mismo grupo que MarÍa Sol. Pensábamos hacer recv del mismo socket en varios hilos, dado que pensábamos que por cada responsabilidad había que crear un hilo, de modo que se permita la ejecución de tareas en forma paralela.

Por ejemplo en Kernel, pensábamos un hilo que se encargara de manejar recursos, otro que se encargara de la replanificacion, etc. Todos estos hilos si se tenian que conectar al mismo modulo lo hacian a través del mismo socket. Después nos dimos cuenta de que estos múltiples recv podrían ocasionar una condición de carrera, interfiriendo entre sí (quizàs el recv() del manejo de recursos toma el paquete que deberia recibir el hilo de replanificacion, por ejemplo).

A raíz de lo que nos sugerís, te preguntamos: deberíamos crear un socket por cada hilo? Quizás convendría conectar un módulo a una misma IP por distintos puertos y, de esa manera, genera sockets distintos de conexión? Si es así, cada hilo se conectaría a un mismo módulo por diferentes sockets y no correríamos riesgo de que interfirieran los recv () entre sí! 😵‍💫😵‍💫

Desde ya, muchas gracias a todos!

LeandroCarbajales commented 3 months ago

Buenas @GabrielBorre ! Lo de tener varios hilos en Kernel está perfecto ya que el mismo realiza muchas tareas concurrentemente. Pero capaz se estén "repartiendo" estas tareas de una forma que se choquen con los recv() cuando esto no es necesario. O capaz estén dividiendo en hilos algunas tareas que pueden hacerse secuencialmente.

Antes de seguir, te confirmo que una solución posible es como bien proponés que por cada mensaje crear una conexión nueva (no necesitan distintos puertos, solamente distintos sockets), de esta manera todos los hilos harían send() y recv() sobre sockets distintos. De hecho, esta es la manera en que normalmente trabajan los clientes y servidores a escala (también llamadas conexiones efímeras porque hacen send()recv() y ya ese socket se cierra)

Sin embargo esa estrategia es overkill para la gran mayoría de las conexiones que plantea el trabajo y seguramente podamos plantear algo más simple.

Vayamos con algunos casos particulares en lugar de ejemplos así los vamos resolviendo. Específicamente me hablaste del hilo de manejo de recursos pudiendo tomar el paquete que debería recibir el hilo de planificación: Por qué este hilo esperaría un mensaje del mismo socket que el de replanificación? (asumo dispatch de CPU), qué hacen con esa respuesta?

En qué otro socket tienen más de 1 hilo esperando por el mismo?

GabrielBorre commented 3 months ago

Buenas Lean! Muchas gracias por tu respuesta!

Efectivamente, el socket al cual conectaríamos los hilos de manejo de recursos y replanificación serían al socket de dispatch de CPU. A partir de esta conexión con dicho socket, necesitamos recibir los procesos que la CPU desaloja de su ejecución. Por eso mismo, los pensábamos vincular al mismo socket, si bien el motivo de desalojo para los procesos que recibe un hilo es distinto al motivo que recibe el otro.

El hilo del manejo de los recursos recibiría procesos desalojados por motivo de desalojo WAIT o SIGNAL, mientras que el de replanificación recibiría procesos desalojados por FIN_QUANTUM o similares. El motivo de desalojo es un atributo que incluimos dentro del struct que representa al proceso

Lo que estábamos pensando es que exista un hilo que se encargue de recibir los procesos que se obtienen desde el socket de la CPU dispatch y que, en función del motivo de desalojo, cree hilos que ejecuten las tareas correspondientes a ese motivo.

Mando un ejemplo en pseudocódigo para que se entienda la idea:

void *recibir_proceso()
{
    t_buffer *buffer = malloc(sizeof(t_buffer));
    t_pcb *pcb;
    recv(socket_cpu_dispatch, &(buffer->size), sizeof(int), MSG_WAITALL);
    buffer->stream = malloc(sizeof(buffer->size));
    recv(socket_cpu_dispatch, buffer->stream, buffer->size, MSG_WAITALL);
    pcb = serializar_pcb(buffer);
    switch (pcb->motivo_desalojo)
    {
    case WAIT:
        pthread_t hilo_manejo_recursos
            pthread_create(&hilo_manejo_recursos, NULL, manejo_recursos, pcb);
    }
}

Con respecto a tu segunda pregunta: por ahora no pensamos que ningún otro socket tiene más de un hilo que se conecte por este.

Por eso mismo, nos surgió una duda con esa parte del Kernel.

Muchísimas gracias por tu ayuda!

LeandroCarbajales commented 3 months ago

Buenas! Excelente, la solución que planteas está mejor encaminada. Te tiro un tip más, para la mayoría de los motivos que te devuelven el proceso (por no decir todos), no es necesario crear un hilo para atenderlo. Por ejemplo ese mismo caso del WAIT, indefectiblemente antes de replanificar tenés que procesar el WAIT para saber si el proceso se bloquea (obligando a replanificar) o no (siga ejecutando el mismo). La lógica es secuencial por lo que tranquilamente se puede realizar en el mismo hilo.

GabrielBorre commented 3 months ago

Lean, muchísimas gracias por tu respuesta!!

Trataremos de encarar el problema por ese lado entonces, teniendo en cuenta que hay tareas que se van a realizar de manera secuencial y, por ende, no es necesaria la inclusión de hilos que ejecuten este tipo de rutinas secuenciales.

Nuevamente, muchas gracias!

LeandroCarbajales commented 3 months ago

Excelente! Si la duda está resuelta les pido que cierren el issue :) Cualquier cosa lo vuelven a abrir y/o acérquense mañana a soporte (yo estaré en campus)

MariaSolGaraventa commented 3 months ago

Cierro el issue por ahora, cualquier cosa lo volveremos a abrir. Muchas gracias por las respuestas! 💙