Multiplexare I/O
Am întâlnit trei tipuri de apeluri blocante, care sunt de fapt citiri din descriptori (de sockeți sau fișiere):
accept()- citire de pe socketul inactiv pe care ascultă serverulrecv()/recvfrom()- citire de pe sockeți activiscanf()/gets()/read(0, ...)- citire de la tastatură.
Intalnim o problemă comună: un program se află blocat într-o citire pe un descriptor, dar primește date pe un alt descriptor.
Ce facem daca vrem sa monitorizam trei socketi in acelasi timp pentru date? Avem nevoie de un mecanism care să ne permită să citim exact de pe descriptorul pe care au venit date.
Soluția este reprezentată de funcția poll(), care ajută la controlarea mai multor descriptori (de fișiere sau sockeți) în același timp.
#include <poll.h>
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
Argumentele funcției select():
fds- mulțimea de file descriptori monitorizaținfds- numărul de elemente din vectorulfdstimeout- timpul maxim în care apelulpoll()trebuie să întoarcă, exprimat în milisecunde. (o valoare negativă specifică o așteptare indefinită până la apariția unui eveniment, în timp ce o valoare nulă presupune întoarce imediată a apelului depoll()).
Structura pollfd este definită în sys/poll.h:
#include <sys/poll.h>
struct pollfd {
int fd; /* file descriptor */
short events; /* evenimente solicitate */
short revents; /* evenimente apărute */
};
În cadrul structurii pollfd avem:
-
eventseste o mască de biți în care se specifică evenimentele urmărite depollpentru descriptorulfd:POLLIN- există date ce pot fi cititePOLLOUT- se pot scrie date
-
reventseste, de asemenea, o mască de biți completată de kernel- cu evenimentele apărute în momentul în care apelul se întoarce(
POLLIN,POLLOUT) - cu valori predefinite (
POLLERR,POLLHUP,POLLNVAL) pentru situații speciale.
- cu evenimentele apărute în momentul în care apelul se întoarce(
În caz de succes, funcția returnează un număr diferit de zero reprezentând numărul de structuri pentru care revents nu e zero (cu alte cuvinte toți descriptorii cu evenimente sau erori).
Se returnează 0 dacă a expirat timpul (timeout milisecunde) și nu a fost selectat nici un descriptor.
Exemplu de folosire poll
#define MAX_PFDS 32
[...]
struct pollfd pfds[MAX_PFDS];
int nfds;
int listenfd, sockfd; /* listener socket; connection socket */
nfds = 0;
/* read user data from standard input */
pfds[nfds].fd = STDIN_FILENO;
pfds[nfds].events = POLLIN;
nfds++;
/* TODO ... create server socket (listener) */
/* add listener socket */
pfds[nfds].fd = listenfd
pfds[nfds].events = POLLIN;
nfds++;
while (1) { /* server loop */
/* wait for readiness notification */
poll(pfds, nfds, -1);
if ((pfds[1].revents & POLLIN) != 0) {
/* TODO ... handle new connection */
}
else if ((pfds[0].revents & POLLIN) != 0) {
/* TODO ... read user data from standard input */
}
else {
/* TODO ... handle message on connection sockets */
}
}
[...]