next up previous contents
Next: O_NONBLOCK + sigaction() Up: Komunikacja asynchroniczna Previous: Komunikacja asynchroniczna   Contents

O_NONBLOCK + poll()/select()

Dwiema funkcjami służącymi do badania stanu deskryptorów są select() oraz poll(). Z tą pierwszą spotkaliśmy się już w jednej z wcześniejszych części cyklu. Teraz warto więc wspomnieć o funkcji poll() (2.1.23+), która tak naprawdę jest tylko odmianą select(). Jedni wolą używać select(), innym bardziej wygodnie korzysta się z poll(). Najlepiej spróbować samemu:
  #include <sys/poll.h>
  
  int poll(struct pollfd *ufds, unsigned int nfds, int timeout);
  
Funkcja zwraca ilość deskryptorów, które zmieniły swój stan albo 0 jeśli upłynął limit czasowy. Błąd to -1.

Szybki przykład:

    struct pollfd pfd[2];
  
    pfd[0].fd = 0;
    pfd[0].events = POLLIN;
    pfd[0].revents = 0;
    pfd[1].fd = 1;
    pfd[1].events = POLLOUT;
    pfd[1].revents = 0;
  
    switch (poll (pfd, 2, 10000))
      {
      case -1:
        {
          perror ("poll()");
          exit (1);
        }
      case 0:
        {
          printf ("Limit czasowy uplynal.\n");
          exit (2);
        }
      default:
        {
          if (pfd[0].revents == POLLIN || pfd[1].revents == POLLOUT)
            {
              printf ("Jeden z deskryptorow zmienil stan.\n");
              exit (0);
            }
          else
            {
              printf ("Blad.\n");
              exit (3);
            }
        }
      }
  }
  
OK ale w jaki sposób zaprzęgnąć funkcje przepytujące do rozwiązania problemu nasłuchu na kilku gniazdach naraz ? Robi się to w ten sposób;
   /* Tworzymy gniazda (socket())                                       */
   ....
  
   /* Każde z gniazd przestawiamy w tryb nieblokujący w ten sposób      */
   fcntl (d, F_SETFL, O_NONBLOCK);
  
   /* Zmuszamy gniazda do nasłuchu (listen())                           */
   ...
  
   /* Wchodzimy do głównej pętli obsługującej połączenia nadchodzące    */
   while (1) {
     FD_SET(d, &fds);    /* Tą operację powtarzamy dla każdego gniazda  */     
     ...
     
     /* Dopiero w tym miejscu wykonywanie programu zostanie wstrzymane. */
     /* Funkcja select() nie zwróci bowiem sterowania dopóki któreś z   */
     /* gniazd nie zmieni swego stanu, co będzie oznaczalo, że          */
     /* najprawdopodobniej dobija sie do nas jakiś klient.              */
     ret = select(..., fds, ..., ..., ...);
  
     /* Jeśli dotarliśmy do tego miejsca to znaczy, że na KTÓRYMŚ z     */
     /* gniazd oczekuje klient do obsłużenia.                           */
     if (ret) {
       for (...) {     /* W pętli sprawdzamy, KTÓRE gniazdo jest gotowe */
         if (ISSET(d)) {
           accept (d, ...);
           obsłuż_połączenie (d);
         }
       }
     } else printf ("Upłynął limit czasowy.");    
   }
  

Powyższe rozwiązanie będzie spełniało swoje zadanie ale nadal nie jest idealne. Zauważyliśmy pewnie, że w pewnym momencie program jest mimo wszystko wstrzymywany na bliżej nieokreślony czas (select()). Oczekuje on bezczynnnie na nadejście połączenia. Byłoby dużo lepiej gdybyśmy mogli w tym czasie robić inne rzeczy. Taką możliwość zapewni nam połączenie gniazd nieblokujących z obsługą sygnałów.


next up previous contents
Next: O_NONBLOCK + sigaction() Up: Komunikacja asynchroniczna Previous: Komunikacja asynchroniczna   Contents
Paweł Niewiadomski
2000-10-17