Informa que está listo por que vuelve.
select
espera los eventos que normalmente están fuera del control de su programa. En esencia, al llamar al select
, su programa dice "No tengo nada que hacer hasta ... suspenda mi proceso".
La condición que especifique es un conjunto de eventos, cualquiera de los cuales lo despertará. Por ejemplo, si está descargando algo, su bucle tendría que esperar a que lleguen nuevos datos, un tiempo de espera para que ocurra si la transferencia está bloqueada, o el usuario para interrumpir, que es exactamente lo que select
hace. Cuando tiene múltiples descargas, los datos que llegan a cualquiera de las conexiones desencadenan actividad en su programa (necesita escribir los datos en el disco), por lo que daría una lista de todas las conexiones de descarga a select
en la lista de descriptores de archivos para mirar para "leer".
Cuando carga datos en algún lugar al mismo tiempo, nuevamente usa select
para ver si la conexión acepta actualmente datos. Si el otro lado está en acceso telefónico, reconocerá los datos solo lentamente, de modo que su búfer de envío local esté siempre lleno, y cualquier intento de escribir más datos se bloquearía hasta que el espacio del búfer esté disponible o fallará. Al pasar el descriptor de archivo al que enviamos al select
como un descriptor de "escritura", se nos notifica tan pronto como el espacio de búfer está disponible para el envío.
La idea general es que su programa se convierta en controlado por eventos, es decir, reacciona a eventos externos desde un bucle de mensaje común en lugar de realizar operaciones secuenciales. Usted le dice al kernel que "este es el conjunto de eventos para el cual quiero hacer algo", y el kernel le proporciona un conjunto de eventos que se han producido. Es bastante común que ocurran dos eventos simultáneamente; por ejemplo, se incluyó un reconocimiento de TCP en un paquete de datos, esto puede hacer que el mismo fd sea legible (datos disponibles) y grabable (los datos confirmados se han eliminado del búfer de envío), por lo que debe estar preparado para manejar todos los eventos antes de llamar de nuevo al select
.
Uno de los puntos más importantes es que select
básicamente le da una promesa que una invocación de read
o write
no bloqueará, sin hacer ningún tipo de garantía sobre la propia llamada.Por ejemplo, si hay disponible un byte de espacio en el búfer, puede intentar escribir 10 bytes, y el kernel volverá y dirá "He escrito 1 byte", por lo que debe estar preparado para manejar este caso también. Un enfoque típico es tener un buffer "data para escribir en este fd", y mientras no esté vacío, el fd se agrega al conjunto de escritura, y el evento "writeable" se maneja intentando escribir todo los datos actualmente en el búfer. Si el búfer está vacío después, bien, de lo contrario, simplemente espere de nuevo "writeable".
El conjunto "excepcional" rara vez se usa: se utiliza para protocolos que tienen datos fuera de banda donde es posible bloquear la transferencia de datos, mientras que otros datos deben pasar. Si su programa no puede aceptar actualmente datos de un descriptor de archivo "legible" (por ejemplo, está descargando y el disco está lleno), no desea incluir el descriptor en el conjunto "legible" porque no puede manejar el evento. y select
volvería inmediatamente si se invoca de nuevo. Si el receptor incluye el fd en el conjunto "excepcional", y el emisor solicita a su pila de IP que envíe un paquete con datos "urgentes", el receptor se despierta y puede decidir descartar los datos no controlados y volver a sincronizarse con el remitente. . El protocolo telnet
usa esto, por ejemplo, para el manejo de Ctrl-C. A menos que esté diseñando un protocolo que requiera tal característica, puede dejar esto sin daños.
obligatorio ejemplo de código:
#include <sys/types.h>
#include <sys/select.h>
#include <unistd.h>
#include <stdbool.h>
static inline int max(int lhs, int rhs) {
if(lhs > rhs)
return lhs;
else
return rhs;
}
void copy(int from, int to) {
char buffer[10];
int readp = 0;
int writep = 0;
bool eof = false;
for(;;) {
fd_set readfds, writefds;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
int ravail, wavail;
if(readp < writep) {
ravail = writep - readp - 1;
wavail = sizeof buffer - writep;
}
else {
ravail = sizeof buffer - readp;
wavail = readp - writep;
}
if(!eof && ravail)
FD_SET(from, &readfds);
if(wavail)
FD_SET(to, &writefds);
else if(eof)
break;
int rc = select(max(from,to)+1, &readfds, &writefds, NULL, NULL);
if(rc == -1)
break;
if(FD_ISSET(from, &readfds))
{
ssize_t nread = read(from, &buffer[readp], ravail);
if(nread < 1)
eof = true;
readp = readp + nread;
}
if(FD_ISSET(to, &writefds))
{
ssize_t nwritten = write(to, &buffer[writep], wavail);
if(nwritten < 1)
break;
writep = writep + nwritten;
}
if(readp == sizeof buffer && writep != 0)
readp = 0;
if(writep == sizeof buffer)
writep = 0;
}
}
Tratamos de leer si tenemos espacio de búfer disponible y no había fin de archivo o error en el lado de lectura, y se intenta escribir si tenemos datos en el buffer; si se llega al final del archivo y el búfer está vacío, entonces hemos terminado.
Este código se comportará claramente por debajo de lo óptimo (es código de ejemplo), pero debería poder ver que es aceptable que el kernel haga menos de lo que pedimos tanto en lecturas como escrituras, en cuyo caso simplemente volvemos y diga "siempre que esté listo", y que nunca leemos o escribimos sin preguntar si se bloqueará.
http://beej.us/guide/bgnet/output/html/multipage/selectman.html –
@NikolaiNFetissov - Desde su enlace, 'Después de que select() regrese, los valores en los conjuntos se cambiarán para mostrar qué están listos para leer o escribir, y que tienen excepciones. Entonces, ¿qué causó la devolución de 'select()' que nos dijo que el socket está listo para leer? Eso es lo que no entiendo – Mike
Cuando la pila de red del núcleo detecta que hay un evento pendiente en cualquiera de los descriptores de socket, su proceso se despierta desde la espera y 'select' regresa. Los conjuntos de FD son parámetros de entrada y salida; usted le dice al núcleo en qué le interesa, le dice qué sucedió. –