Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Multiplexare I/O

Am întâlnit trei tipuri de apeluri blocante, care sunt de fapt citiri din descriptori (de sockeți sau fișiere):

  1. accept() - citire de pe socketul inactiv pe care ascultă serverul
  2. recv() / recvfrom() - citire de pe sockeți activi
  3. scanf() / 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():

  1. fds - mulțimea de file descriptori monitorizați
  2. nfds - numărul de elemente din vectorul fds
  3. timeout - timpul maxim în care apelul poll() 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 de poll()).

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:

  • events este o mască de biți în care se specifică evenimentele urmărite de poll pentru descriptorul fd:

    • POLLIN - există date ce pot fi citite
    • POLLOUT - se pot scrie date
  • revents este, 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.

Î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 */
    }
}
[...]