select_tut

SELECT_TUT(2)   Ð ÑководÑÑво пÑогÑаммиÑÑа Linux   SELECT_TUT(2)



ÐÐЯ
       select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO -
       многопоÑоÑнÑй ÑинÑÑоннÑй ввод-вÑвод

ÐÐÐÐÐ
       /* Ð ÑооÑвеÑÑÑвии Ñ POSIX.1-2001, POSIX.1-2008 */
       #include <sys/select.h>

       /* Ð ÑооÑвеÑÑÑвие Ñ Ð±Ð¾Ð»ÐµÐµ Ñанними ÑÑандаÑÑами */
       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

       int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *utimeout);

       void FD_CLR(int fd, fd_set *set);
       int  FD_ISSET(int fd, fd_set *set);
       void FD_SET(int fd, fd_set *set);
       void FD_ZERO(fd_set *set);

       #include <sys/select.h>

       int pselect(int nfds, fd_set *readfds, fd_set *writefds,
                   fd_set *exceptfds, const struct timespec *ntimeout,
                   const sigset_t *sigmask);

   ТÑÐµÐ±Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¼Ð°ÐºÑоÑа ÑеÑÑиÑÐ¾Ð²Ð°Ð½Ð¸Ñ ÑвойÑÑв Ð´Ð»Ñ glibc
   (Ñм. feature_test_macros(7)):

       pselect(): _POSIX_C_SOURCE >= 200112L

ÐÐÐСÐÐÐÐ
       ÐÑзов select() (или pselect()) иÑполÑзÑеÑÑÑ Ð´Ð»Ñ
       ÑÑÑекÑивного ÑÐ»ÐµÐ¶ÐµÐ½Ð¸Ñ Ð·Ð° неÑколÑкими
       ÑайловÑми деÑкÑипÑоÑами â Ð´Ð»Ñ Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ,
       когда какой-Ñо из Ð½Ð¸Ñ Ð½Ðµ ÑÑÐ°Ð½ÐµÑ Â«Ð³Ð¾Ñов», Ñо
       еÑÑÑ Ð¿Ð¾ÑвиÑÑÑ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ÑÑÑ ÑÑениÑ-запиÑи
       даннÑÑ, или Ñ ÑайловÑм деÑкÑипÑоÑом не
       Ð²Ð¾Ð·Ð½Ð¸ÐºÐ½ÐµÑ Â«Ð¸ÑклÑÑиÑелÑÐ½Ð°Ñ ÑиÑÑаÑиÑ».

       ÐÑновнÑе паÑамеÑÑÑ Ð·Ð°Ð´Ð°ÑÑÑÑ Ð² виде «набоÑа»
       ÑайловÑÑ Ð´ÐµÑкÑипÑоÑов: readfds, writefds и exceptfds.
       ÐаждÑй Ð½Ð°Ð±Ð¾Ñ Ð¸Ð¼ÐµÐµÑ Ñип fd_set и его
       ÑодеÑжимое можно изменÑÑÑ Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ
       макÑоÑов FD_CLR(), FD_ISSET(), FD_SET() и FD_ZERO(). ÐÑи
       Ñоздании нового набоÑа ÑнаÑала его нÑжно
       оÑиÑÑиÑÑ Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ FD_ZERO(). ÐÑзов select() изменÑеÑ
       ÑодеÑжимое набоÑов в ÑооÑвеÑÑÑвии Ñ
       пÑавилами, опиÑаннÑми далее; поÑле вÑзова
       select() Ð²Ñ Ð¼Ð¾Ð¶ÐµÑе пÑовеÑиÑÑ ÑÑÑеÑÑвÑÐµÑ Ð»Ð¸ еÑÑ
       ÑайловÑй деÑкÑипÑÐ¾Ñ Ð² набоÑе Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ
       макÑоÑа FD_ISSET(). ÐакÑÐ¾Ñ FD_ISSET() возвÑаÑаеÑ
       ненÑлевое знаÑение, еÑли ÑказаннÑй
       ÑайловÑй деÑкÑипÑÐ¾Ñ Ð¿ÑиÑÑÑÑÑвÑÐµÑ Ð² набоÑе и
       нолÑ, еÑли оÑÑÑÑÑÑвÑеÑ. ÐакÑÐ¾Ñ FD_CLR() ÑдалÑеÑ
       ÑайловÑй деÑкÑипÑÐ¾Ñ Ð¸Ð· набоÑа.

   ÐÑгÑменÑÑ
       readfds
              ÐÑÐ¾Ñ Ð½Ð°Ð±Ð¾Ñ ÑлÑÐ¶Ð¸Ñ Ð´Ð»Ñ ÑÐ»ÐµÐ¶ÐµÐ½Ð¸Ñ Ð·Ð°
              поÑвлением даннÑÑ, доÑÑÑпнÑÑ Ð´Ð»Ñ ÑÑениÑ
              из лÑбого Ñайлового деÑкÑипÑоÑа. ÐоÑле
              возвÑаÑа из select() в readfds оÑÑаÑÑÑÑ ÑолÑко
              Ñе деÑкÑипÑоÑÑ Ñайлов, из коÑоÑÑÑ
              возможно немедленное ÑÑение.

       writefds
              ÐÑÐ¾Ñ Ð½Ð°Ð±Ð¾Ñ ÑлÑÐ¶Ð¸Ñ Ð´Ð»Ñ ÑÐ»ÐµÐ¶ÐµÐ½Ð¸Ñ Ð·Ð°
              поÑвлением меÑÑа Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи даннÑÑ Ð²
              лÑбой из ÑайловÑÑ Ð´ÐµÑкÑипÑоÑов набоÑа.
              ÐоÑле возвÑаÑа из select() в writefds оÑÑаÑÑÑÑ
              ÑолÑко Ñе ÑайловÑе деÑкÑипÑоÑÑ, в коÑоÑÑе
              возможна Ð½ÐµÐ¼ÐµÐ´Ð»ÐµÐ½Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑ.

       exceptfds
              ÐÑÐ¾Ñ Ð½Ð°Ð±Ð¾Ñ ÑлÑÐ¶Ð¸Ñ Ð´Ð»Ñ ÑÐ»ÐµÐ¶ÐµÐ½Ð¸Ñ Ð·Ð°
              «иÑклÑÑиÑелÑнÑми ÑиÑÑаÑиÑми». Ðа Ñамом
              деле, оÑÑлеживаеÑÑÑ ÑолÑко одна
              ÑаÑпÑоÑÑÑанÑÐ½Ð½Ð°Ñ Ð¸ÑклÑÑиÑелÑÐ½Ð°Ñ ÑиÑÑаÑиÑ:
              доÑÑÑпноÑÑÑ Ð²Ð½ÐµÐ¿Ð¾ÑоÑнÑÑ (out-of-band â OOB)
              даннÑÑ Ð´Ð»Ñ ÑÑÐµÐ½Ð¸Ñ Ð¸Ð· ÑокеÑа TCP. Ðолее
              подÑобно о даннÑÑ OOB ÑмоÑÑиÑе в recv(2),
              send(2) и tcp(7). ÐÑÑÐ³Ð°Ñ Ð¼ÐµÐ½ÐµÐµ ÑаÑпÑоÑÑÑанÑннаÑ
              ÑиÑÑаÑиÑ: select(2) ÑказÑÐ²Ð°ÐµÑ Ð½Ð°
              иÑклÑÑиÑелÑнÑÑ ÑиÑÑаÑÐ¸Ñ Ñ
              пÑевдоÑеÑминалом в пакеÑном Ñежиме;
              Ñм. tty_ioctl(4). ÐоÑле возвÑаÑа из select() в
              exceptfds оÑÑаÑÑÑÑ ÑолÑко Ñе ÑайловÑе
              деÑкÑипÑоÑÑ, в коÑоÑÑÑ Ð¿ÑоизоÑла
              иÑклÑÑиÑелÑÐ½Ð°Ñ ÑиÑÑаÑиÑ.

       nfds   ÐÑедÑÑавлÑÐµÑ Ñобой Ñелое ÑиÑло, на
              единиÑÑ Ð±Ð¾Ð»ÑÑее макÑималÑного
              Ñайлового деÑкÑипÑоÑа в лÑбом из
              набоÑов. ÐÑÑгими Ñловами, пÑи
              добавлении ÑайловÑÑ Ð´ÐµÑкÑипÑоÑов в
              набоÑÑ Ð²Ð°Ð¼ необÑодимо вÑÑиÑлÑÑÑ
              макÑималÑное Ñелое знаÑение лÑбого
              из ниÑ, а заÑем ÑвелиÑиваÑÑ ÑÑо знаÑение
              на единиÑÑ Ð¸ пеÑедаваÑÑ Ð² nfds.

       utimeout
              ÐÑÐ¾Ñ Ð°ÑгÑÐ¼ÐµÐ½Ñ Ð·Ð°Ð´Ð°ÑÑ Ð½Ð°Ð¸Ð±Ð¾Ð»ÑÑее вÑемÑ,
              коÑоÑое вÑзов select() бÑÐ´ÐµÑ Ð¾Ð¶Ð¸Ð´Ð°ÑÑ
              ÑобÑÑий, по пÑоÑеÑÑвии коÑоÑого завеÑÑиÑ
              ÑабоÑÑ, даже еÑли ниÑего не пÑоизойдÑÑ.
              ÐÑли знаÑение ÑÑого аÑгÑменÑа Ñавно NULL,
              Ñо select() бÑÐ´ÐµÑ Ð¾Ð¶Ð¸Ð´Ð°ÑÑ Ð±ÐµÑконеÑно.
              ÐнаÑение utimeout Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ ÑÑÑановлено в
              Ð½Ð¾Ð»Ñ ÑекÑнд; в ÑÑом ÑлÑÑае select() возвÑаÑиÑ
              ÑпÑавление немедленно, Ñ Ð¸Ð½ÑоÑмаÑией
              о гоÑовноÑÑи ÑайловÑÑ Ð´ÐµÑкÑипÑоÑов на
              Ð¼Ð¾Ð¼ÐµÐ½Ñ Ð²Ñзова. СÑÑÑкÑÑÑа struct timeval
              опÑеделена ÑледÑÑÑим обÑазом:

                  struct timeval {
                      time_t tv_sec;    /* ÑекÑÐ½Ð´Ñ */
                      long tv_usec;     /* микÑоÑекÑÐ½Ð´Ñ */
                  };

       ntimeout
              ÐÑÐ¾Ñ Ð°ÑгÑÐ¼ÐµÐ½Ñ pselect() Ð¸Ð¼ÐµÐµÑ Ñо же
              знаÑение, ÑÑо и utimeout, но ÑÑÑÑкÑÑÑа struct
              timespec позволÑÐµÑ ÑказÑваÑÑ Ð²ÑÐµÐ¼Ñ Ñ ÑоÑноÑÑÑÑ
              до наноÑекÑнд:

                  struct timespec {
                      long tv_sec;    /* ÑекÑÐ½Ð´Ñ */
                      long tv_nsec;   /* наноÑекÑÐ½Ð´Ñ */
                  };

       sigmask
              ÐÑÐ¾Ñ Ð°ÑгÑÐ¼ÐµÐ½Ñ ÑодеÑÐ¶Ð¸Ñ Ð½Ð°Ð±Ð¾Ñ Ñигналов,
              коÑоÑÑе ÑдÑо должно ÑазблокиÑоваÑÑ (Ñо
              еÑÑÑ ÑдалиÑÑ Ð¸Ð· маÑки Ñигналов
              вÑзÑваÑÑей ниÑи) на вÑемÑ, пока
              вÑзÑваÑÑий заблокиÑован в вÑзове
              pselect() (Ñм. sigaddset(3) и sigprocmask(2)). РкаÑеÑÑве
              аÑгÑменÑа Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¿ÐµÑедано знаÑение
              NULL â вÑзов не изменÑÐµÑ Ð¼Ð°ÑÐºÑ Ñигналов
              пÑи вÑоде и вÑÑоде из ÑÑнкÑии. То еÑÑÑ
              pselect() ведÑÑ ÑÐµÐ±Ñ ÐºÐ°Ðº select().

   ÐомбиниÑование ÑобÑÑий Ñигналов и даннÑÑ
       ÐÑзов pselect() полезен как Ð´Ð»Ñ Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ
       Ñигнала, Ñак и Ð´Ð»Ñ Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð³Ð¾ÑовноÑÑи
       Ñайлового деÑкÑипÑоÑа Ð´Ð»Ñ Ð²Ð²Ð¾Ð´Ð°-вÑвода.
       ÐÑогÑаммÑ, пÑинимаÑÑие ÑигналÑ, как пÑавило,
       лиÑÑ Ð²ÑÑÑавлÑÑÑ Ð² обÑабоÑÑике Ñигнала
       глобалÑнÑй Ñлаг, коÑоÑÑй ознаÑаеÑ, ÑÑо
       ÑÑебÑеÑÑÑ Ð¾Ð±ÑабоÑка ÑобÑÑÐ¸Ñ Ð² главном Ñикле
       пÑогÑаммÑ. ÐоÑвление Ñигнала заÑÑÐ°Ð²Ð¸Ñ Ð²Ñзов
       select() (или pselect()) веÑнÑÑÑ ÑпÑавление
       вÑзвавÑей пÑогÑамме; пÑи ÑÑом errno бÑдеÑ
       пÑиÑвоено EINTR. ÐÑо поведение пÑодикÑовано
       необÑодимоÑÑÑÑ Ð¾Ð±ÑабоÑки Ñигналов в
       главном Ñикле пÑогÑÐ°Ð¼Ð¼Ñ Ð²Ð¾ избежание
       беÑконеÑной блокиÑовки select(). Рглавном
       Ñикле пÑогÑÐ°Ð¼Ð¼Ñ Ð´Ð¾Ð»Ð¶Ð½Ð¾ бÑÑÑ ÑÑловие,
       пÑовеÑÑÑÑее глобалÑнÑй Ñлаг. ÐозникаеÑ
       вопÑоÑ: а ÑÑо еÑли Ñигнал пÑидÑÑ Ð¿Ð¾Ñле
       пÑовеÑки ÑÑого ÑÑловиÑ, но до вÑзова select()? Ð
       ÑÑом ÑлÑÑае пÑогÑамма навÑегда оÑÑанеÑÑÑ Ð²
       select(), ÑоÑÑ Ð¸ еÑÑÑ Ð¾Ð¶Ð¸Ð´Ð°ÑÑее ÑобÑÑие. ÐлÑ
       ÑазÑеÑÐµÐ½Ð¸Ñ ÑÑой пÑÐ¾Ð±Ð»ÐµÐ¼Ñ ÑÑÑеÑÑвÑÐµÑ Ð²Ñзов
       pselect(). ÐÑÐ¾Ñ Ð²Ñзов можно иÑполÑзоваÑÑ Ð´Ð»Ñ
       ÑÑÑановки в ÑигналÑной маÑке Ñигналов,
       коÑоÑÑе пÑинимаÑÑÑÑ ÑолÑко внÑÑÑи вÑзова
       pselect(). ÐапÑимеÑ, пÑедположим ÑÑо инÑеÑеÑÑÑÑее
       Ð½Ð°Ñ ÑобÑÑие â ÑÑо завеÑÑение доÑеÑнего
       пÑоÑеÑÑа. ÐеÑед запÑÑком главного Ñикла
       заблокиÑÑем SIGCHLD Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ sigprocmask(2). ÐаÑ
       вÑзов pselect() ÑазблокиÑÑÐµÑ SIGCHLD, Ñказав пÑÑÑÑÑ
       маÑÐºÑ Ñигналов. ÐÑогÑамма бÑÐ´ÐµÑ Ð²ÑглÑдеÑÑ
       Ñак:

       static volatile sig_atomic_t got_SIGCHLD = 0;

       static void
       child_sig_handler(int sig)
       {
           got_SIGCHLD = 1;
       }

       int
       main(int argc, char *argv[])
       {
           sigset_t sigmask, empty_mask;
           struct sigaction sa;
           fd_set readfds, writefds, exceptfds;
           int r;

           sigemptyset(&sigmask);
           sigaddset(&sigmask, SIGCHLD);
           if (sigprocmask(SIG_BLOCK, &sigmask, NULL) == -1) {
               perror("sigprocmask");
               exit(EXIT_FAILURE);
           }

           sa.sa_flags = 0;
           sa.sa_handler = child_sig_handler;
           sigemptyset(&sa.sa_mask);
           if (sigaction(SIGCHLD, &sa, NULL) == -1) {
               perror("sigaction");
               exit(EXIT_FAILURE);
           }

           sigemptyset(&empty_mask);

           for (;;) {          /* главнÑй Ñикл */
               /* ÐниÑиализаÑÐ¸Ñ readfds, writefds и exceptfds
                  до вÑзова pselect() (код не показан). */

               r = pselect(nfds, &readfds, &writefds, &exceptfds,
                           NULL, &empty_mask);
               if (r == -1 && errno != EINTR) {
                   /* обÑабоÑка оÑибки */
               }

               if (got_SIGCHLD) {
                   got_SIGCHLD = 0;

                   /* ÐдеÑÑ Ð¾Ð±ÑабоÑка ÑигналÑного ÑобÑÑиÑ; напÑÐ¸Ð¼ÐµÑ Ñ
                      помоÑÑÑ wait() Ð´Ð»Ñ Ð·Ð°Ð²ÐµÑÑÐµÐ½Ð¸Ñ Ð¿Ð¾Ñомком (код не показан). */
               }

               /* код оÑновной пÑогÑÐ°Ð¼Ð¼Ñ */
           }
       }

   ÐÑакÑика
       ÐÑак, какой пÑок Ð¾Ñ Ð¸ÑполÑÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ select()?
       Разве нелÑÐ·Ñ Ð¿ÑоÑÑо ÑÑиÑÑваÑÑ Ð¸ запиÑÑваÑÑ
       даннÑе в ÑайловÑе деÑкÑипÑоÑÑ ÐºÐ¾Ð³Ð´Ð° ÑÑого
       заÑоÑеÑÑÑ? СмÑÑл иÑполÑÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ select() в Ñом, ÑÑо
       он позволÑÐµÑ ÑÐ»ÐµÐ´Ð¸Ñ Ð·Ð° неÑколÑкими
       деÑкÑипÑоÑами одновÑеменно и коÑÑекÑно
       пеÑеводиÑÑ Ð¿ÑоÑеÑÑ Ð² Ñежим ожиданиÑ, когда
       акÑивноÑÑи не наблÑдаеÑÑÑ. ÐÑогÑаммиÑÑÑ UNIX
       ÑаÑÑо попадаÑÑ Ð² ÑиÑÑаÑиÑ, когда необÑодимо
       обÑабоÑаÑÑ Ð²Ð²Ð¾Ð´-вÑвод из более Ñем одного
       Ñайлового деÑкÑипÑоÑа в Ñо вÑÐµÐ¼Ñ ÐºÐ°Ðº поÑок
       даннÑÑ Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð½ÐµÑавномеÑнÑм. ÐÑли вÑ
       пÑоÑÑо ÑоздадиÑе поÑледоваÑелÑноÑÑÑ Ð²Ñзовов
       read(2) и write(2), Ñо можеÑе попаÑÑÑ Ð² ÑиÑÑаÑиÑ,
       когда один из вÑзовов бÑÐ´ÐµÑ Ð¾Ð¶Ð¸Ð´Ð°ÑÑ
       даннÑе из/в Ñайлового деÑкÑипÑоÑа, в Ñо
       вÑÐµÐ¼Ñ ÐºÐ°Ðº дÑÑгой бÑÐ´ÐµÑ Ð¿ÑоÑÑаиваÑÑ, ÑоÑÑ
       даннÑе Ð´Ð»Ñ Ð½ÐµÐ³Ð¾ Ñже поÑвилиÑÑ. ÐÑзов select()
       позволÑÐµÑ ÑÑÑекÑивно ÑпÑавиÑÑÑÑ Ñ Ñакой
       ÑиÑÑаÑией.

   ÐÑавила иÑполÑзованиÑ
       Ðногие из ÑеÑ, кÑо пÑÑалÑÑ Ð¸ÑполÑзоваÑÑ select(),
       ÑÑалкивалиÑÑ Ñ Ð¿Ð¾Ð²ÐµÐ´ÐµÐ½Ð¸ÐµÐ¼, коÑоÑое ÑÑÑдно
       понÑÑÑ, и коÑоÑое пÑиводило к непеÑеноÑимÑм
       или пÑоÑÑо плоÑим ÑезÑлÑÑаÑам. ÐапÑимеÑ,
       вÑÑепÑÐ¸Ð²ÐµÐ´ÐµÐ½Ð½Ð°Ñ Ð¿ÑогÑамма ÑÑаÑелÑно
       ÑпланиÑована Ñак, ÑÑÐ¾Ð±Ñ Ð½Ð¸ в каком ÑлÑÑае не
       блокиÑоваÑÑÑÑ, ÑоÑÑ Ð´Ð»Ñ ÐµÑ ÑайловÑÑ
       деÑкÑипÑоÑов не ÑÑÑановлен неблокиÑÑÑÑий
       Ñежим. ÐеÑложно пеÑеÑиÑлиÑÑ Ð½Ðµ оÑевиднÑе
       оÑибки, коÑоÑÑе лиÑÐ°Ñ Ð²ÑÐµÑ Ð¿ÑеимÑÑеÑÑв
       иÑполÑÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ select(), поÑÑÐ¾Ð¼Ñ Ð²Ð¾Ñ ÑпиÑок
       оÑновнÑÑ Ð¼Ð¾Ð¼ÐµÐ½Ñов, на коÑоÑÑе нÑжно обÑаÑаÑÑ
       внимание пÑи иÑполÑзовании select().

       1.  ÐÑегда ÑÑаÑайÑеÑÑ Ð¸ÑполÑзоваÑÑ select() без
           ÑÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð²Ñемени ожиданиÑ. ÐаÑа
           пÑогÑамма не должна ниÑего делаÑÑ, еÑли
           Ð½ÐµÑ Ð´Ð°Ð½Ð½ÑÑ. Ðод, завиÑимÑй Ð¾Ñ Ð²Ñемени
           ожиданиÑ, обÑÑно плоÑо пеÑеноÑим и
           Ñложен Ð´Ð»Ñ Ð¾Ñладки.

       2.  ÐÐ»Ñ Ð¿Ð¾Ð²ÑÑÐµÐ½Ð¸Ñ ÑÑÑекÑивноÑÑи знаÑение nfds
           должно пÑавилÑно вÑÑиÑлÑÑÑÑÑ, как ÑÑо
           обÑÑÑнÑлоÑÑ Ð²ÑÑе.

       3.  ФайловÑе деÑкÑипÑоÑÑ Ð½Ðµ должнÑ
           добавлÑÑÑÑÑ Ð² набоÑÑ, еÑли Ð²Ñ Ð½Ðµ планиÑÑеÑе
           поÑле вÑзова select() пÑовеÑÑÑÑ ÑезÑлÑÑÐ°Ñ Ð¸
           ÑооÑвеÑÑÑвÑÑÑим обÑазом ÑеагиÑоваÑÑ.
           СмоÑÑиÑе ÑледÑÑÑее пÑавило.

       4.  ÐоÑле возвÑаÑа из select() Ð´Ð¾Ð»Ð¶Ð½Ñ Ð±ÑÑÑ
           пÑовеÑÐµÐ½Ñ Ð²Ñе ÑайловÑе деÑкÑипÑоÑÑ Ð²Ð¾ вÑеÑ
           набоÑаÑ.

       5.  ÐÑÐ·Ð¾Ð²Ñ read(2), recv(2), write(2) и send(2) не
           обÑзаÑелÑно ÑÑиÑÑваÑÑ/запиÑÑваÑÑ Ð´Ð°Ð½Ð½Ñе в
           полном обÑÑме. Такое, конеÑно,
           возможно пÑи низком ÑÑаÑике или бÑÑÑÑом
           поÑоке, однако пÑоиÑÑÐ¾Ð´Ð¸Ñ Ð´Ð°Ð»ÐµÐºÐ¾ не
           вÑегда. ÐÑ Ð´Ð¾Ð»Ð¶Ð½Ñ ÑаÑÑÑиÑÑваÑÑ, ÑÑо ваÑи
           ÑÑнкÑии полÑÑаÑÑ/оÑпÑавлÑÑÑ ÑолÑко один
           Ð±Ð°Ð¹Ñ Ð·Ð° Ñаз.

       6.  Ðикогда не ÑÑиÑÑвайÑе/запиÑÑвайÑе
           побайÑно, еÑли ÑолÑко Ð²Ñ Ð½Ðµ абÑолÑÑно
           ÑвеÑÐµÐ½Ñ Ð² Ñом, ÑÑо нÑжно обÑабоÑаÑÑ
           неболÑÑой обÑÑм даннÑÑ. ÐÑайне
           неÑÑÑекÑивно ÑÑиÑÑваÑÑ/запиÑÑваÑÑ Ð¼ÐµÐ½ÑÑее
           колиÑеÑÑво байÑ, Ñем Ð²Ñ Ð¼Ð¾Ð¶ÐµÑе помеÑÑиÑÑ Ð²
           бÑÑÐµÑ Ð·Ð° один Ñаз. ÐÑÑеÑÑ Ð² вÑÑепÑиведÑнном
           пÑимеÑе имеÑÑ ÑÐ°Ð·Ð¼ÐµÑ 1024 байÑа, однако
           могÑÑ Ð±ÑÑÑ Ð»ÐµÐ³ÐºÐ¾ ÑвелиÑÐµÐ½Ñ Ð´Ð¾
           макÑималÑного ÑазмеÑа пакеÑа в ваÑей
           локалÑной ÑеÑи.

       7.  ÐÑÐ·Ð¾Ð²Ñ read(2), recv(2), write(2) и send(2) Ñакже как и
           select() могÑÑ Ð²Ð¾Ð·Ð²ÑаÑиÑÑ -1 Ñ errno ÑавнÑм EINTR,
           или errno ÑавнÑм EAGAIN (EWOULDBLOCK). Такие
           ÑиÑÑаÑии Ð´Ð¾Ð»Ð¶Ð½Ñ Ð±ÑÑÑ Ð¿ÑавилÑно обÑабоÑанÑ
           (в вÑÑепÑиведенной пÑогÑамме ÑÑого не
           Ñделано). ÐÑли ваÑа пÑогÑамма не
           ÑобиÑаеÑÑÑ Ð¿ÑинимаÑÑ ÑигналÑ, Ñо
           маловеÑоÑÑно, ÑÑо Ð²Ñ Ð¿Ð¾Ð»ÑÑиÑе EINTR. ÐÑли ваÑа
           пÑогÑамма не иÑполÑзÑÐµÑ Ð½ÐµÐ±Ð»Ð¾ÐºÐ¸ÑÑÑÑий
           ввод-вÑвод, Ñо Ð²Ñ Ð½Ðµ полÑÑиÑе EAGAIN.

       8.  Ðикогда не вÑзÑвайÑе read(2), recv(2), write(2)
           или send(2) Ñ Ð±ÑÑеÑом нÑлевой длинÑ.

       9.  ÐÑли вÑÐ·Ð¾Ð²Ñ read(2), recv(2), write(2) и send(2)
           завеÑÑаÑÑÑÑ Ñ Ð¾Ñибками, оÑлиÑнÑми оÑ
           пеÑеÑиÑленнÑÑ Ð² пÑнкÑе 7. или один из
           вÑзовов ввода веÑнÑл 0, ÑÑо ÑказÑÐ²Ð°ÐµÑ Ð½Ð°
           ÐºÐ¾Ð½ÐµÑ Ñайла, Ñо Ð²Ñ Ð½Ðµ Ð´Ð¾Ð»Ð¶Ð½Ñ Ð¿ÐµÑедаваÑÑ
           ÑÑÐ¾Ñ ÑайловÑй деÑкÑипÑÐ¾Ñ Ð² select() Ñнова. Ð
           пÑимеÑе вÑÑе Ñ Ð½ÐµÐ¼ÐµÐ´Ð»ÐµÐ½Ð½Ð¾ закÑÑваÑ
           ÑайловÑй деÑкÑипÑÐ¾Ñ Ð¸ ÑÑÑÐ°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°Ñ ÐµÐ³Ð¾ в
           -1 Ð´Ð»Ñ Ð¿ÑедоÑвÑаÑÐµÐ½Ð¸Ñ ÐµÐ³Ð¾ вклÑÑÐµÐ½Ð¸Ñ Ð²
           набоÑ.

       10. ÐнаÑение вÑемени Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð´Ð¾Ð»Ð¶Ð½Ð¾ бÑÑÑ
           иниÑиализиÑовано пÑи каждом новом
           вÑзове select(), Ñак как некоÑоÑÑе
           опеÑаÑионнÑе ÑиÑÑÐµÐ¼Ñ Ð¸Ð·Ð¼ÐµÐ½ÑÑÑ Ð·Ð½Ð°Ñение
           ÑÑÑÑкÑÑÑÑ. Ðднако pselect() не изменÑÐµÑ ÑÑÑÑкÑÑÑÑ
           вÑемени ожиданиÑ.

       11. Так как select() изменÑÐµÑ Ð¿ÐµÑеданнÑе набоÑÑ
           ÑайловÑÑ Ð´ÐµÑкÑипÑоÑов, Ñо пÑи
           иÑполÑзовании его в Ñикле набоÑÑ Ð´Ð¾Ð»Ð¶Ð½Ñ
           повÑоÑно иниÑиализиÑоваÑÑÑÑ Ð¿ÐµÑед каждÑм
           вÑзовом.

   ÐмÑлÑÑÐ¸Ñ usleep
       Ð ÑиÑÑемаÑ, не имеÑÑÐ¸Ñ ÑÑнкÑии usleep(3), Ð²Ñ Ð¼Ð¾Ð¶ÐµÑе
       иÑполÑзоваÑÑ select() Ñ ÐºÐ¾Ð½ÐµÑной задеÑжкой и
       без ÑайловÑÑ Ð´ÐµÑкÑипÑоÑов ÑледÑÑÑим обÑазом:

           struct timeval tv;
           tv.tv_sec = 0;
           tv.tv_usec = 200000;  /* 0.2 ÑекÑÐ½Ð´Ñ */
           select(0, NULL, NULL, NULL, &tv);

       Ðднако ÑабоÑа гаÑанÑиÑÑеÑÑÑ ÑолÑко в ÑиÑÑемаÑ
       UNIX.

ÐÐÐÐÐ ÐЩÐÐÐÐÐ ÐÐÐЧÐÐÐÐ
       ÐÑи ÑдаÑно завеÑÑении select() возвÑаÑÐ°ÐµÑ Ð¾Ð±Ñее
       ÑиÑло ÑайловÑÑ Ð´ÐµÑкÑипÑоÑов, коÑоÑÑе оÑÑалиÑÑ
       в набоÑаÑ.

       ÐÑи вÑÑоде из select() по оконÑании вÑемени
       Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð²Ð¾Ð·Ð²ÑаÑаеÑÑÑ Ð½Ð¾Ð»Ñ. ÐÑе набоÑÑ
       ÑайловÑÑ Ð´ÐµÑкÑипÑоÑов бÑдÑÑ Ð¿ÑÑÑÑ (но могÑÑ Ð±ÑÑÑ
       не пÑÑÑÑ Ð½Ð° некоÑоÑÑÑ ÑиÑÑемаÑ).

       ÐнаÑение -1 ÑказÑÐ²Ð°ÐµÑ Ð½Ð° оÑибкÑ, пÑи ÑÑом errno
       ÑÑÑанавливаеÑÑÑ ÑооÑвеÑÑÑвÑÑÑим обÑазом. Ð
       ÑлÑÑае оÑибки ÑодеÑжимое набоÑов и ÑÑÑÑкÑÑÑÑ
       struct timeout не опÑеделено и не должно бÑÑÑ
       иÑполÑзовано. Ðднако вÑзов pselect() никогда
       не изменÑÐµÑ ntimeout.

ÐÐÐÐЧÐÐÐЯ
       РобÑем ÑлÑÑае, вÑе опеÑаÑионнÑе ÑиÑÑемÑ,
       поддеÑживаÑÑие ÑокеÑÑ, поддеÑживаÑÑ Ñакже и
       select(). ÐÑзов select() можно пÑименÑÑÑ Ð´Ð»Ñ
       пеÑеноÑимого и ÑÑÑекÑивного ÑеÑÐµÐ½Ð¸Ñ Ð¼Ð½Ð¾Ð³Ð¸Ñ
       задаÑ, вмеÑÑо коÑоÑого многие пÑогÑаммиÑÑÑ
       пÑÑаÑÑÑÑ Ð¸ÑполÑзоваÑÑ Ð½Ð¸Ñи, веÑвление
       пÑоÑеÑÑов, IPC, ÑигналÑ, Ñазделение памÑÑи и
       дÑÑгие меÑодÑ.

       СиÑÑемнÑй вÑзов poll(2) Ð¸Ð¼ÐµÐµÑ ÑакÑÑ Ð¶Ðµ
       ÑÑнкÑионалÑноÑÑÑ, как и select() и иногда
       более ÑÑÑекÑивен Ð´Ð»Ñ ÑÐ»ÐµÐ¶ÐµÐ½Ð¸Ñ Ð·Ð°
       ÑазÑеженнÑм набоÑом ÑайловÑÑ Ð´ÐµÑкÑипÑоÑов. Ð
       наÑÑоÑÑее вÑÐµÐ¼Ñ Ð¾Ð½ ÑÑал ÑиÑоко ÑаÑпÑоÑÑÑанÑн,
       но иÑÑоÑиÑеÑки ÑвлÑеÑÑÑ Ð¼ÐµÐ½ÐµÐµ пеÑеноÑимÑм Ñем
       select().

       ÐÑогÑаммнÑй инÑеÑÑÐµÐ¹Ñ Linux epoll(7) пÑедоÑÑавлÑеÑ
       более ÑÑÑекÑивнÑй меÑод Ð´Ð»Ñ ÑÐ»ÐµÐ¶ÐµÐ½Ð¸Ñ Ð·Ð°
       болÑÑим колиÑеÑÑвом ÑайловÑÑ Ð´ÐµÑкÑипÑоÑов
       Ñем select(2) и poll(2).

ÐÐ ÐÐÐÐ
       ÐÐ¾Ñ Ð¿ÑимеÑ, коÑоÑÑй лÑÑÑе демонÑÑÑиÑÑеÑ
       возможноÑÑи select(). ÐÑогÑамма оÑÑÑеÑÑвлÑеÑ
       пеÑенапÑавление одного поÑÑа TCP в дÑÑгой.

       #include <stdlib.h>
       #include <stdio.h>
       #include <unistd.h>
       #include <sys/time.h>
       #include <sys/types.h>
       #include <string.h>
       #include <signal.h>
       #include <sys/socket.h>
       #include <netinet/in.h>
       #include <arpa/inet.h>
       #include <errno.h>

       static int forward_port;

       #undef max
       #define max(x,y) ((x) > (y) ? (x) : (y))

       static int
       listen_socket(int listen_port)
       {
           struct sockaddr_in addr;
           int lfd;
           int yes;

           lfd = socket(AF_INET, SOCK_STREAM, 0);
           if (lfd == -1) {
               perror("socket");
               return -1;
           }

           yes = 1;
           if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR,
                   &yes, sizeof(yes)) == -1) {
               perror("setsockopt");
               close(lfd);
               return -1;
           }

           memset(&addr, 0, sizeof(addr));
           addr.sin_port = htons(listen_port);
           addr.sin_family = AF_INET;
           if (bind(lfd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
               perror("bind");
               close(lfd);
               return -1;
           }

           printf("пÑиÑм Ñоединений на поÑÑÑ %d\n", listen_port);
           listen(lfd, 10);
           return lfd;
       }

       static int
       connect_socket(int connect_port, char *address)
       {
           struct sockaddr_in addr;
           int cfd;

           cfd = socket(AF_INET, SOCK_STREAM, 0);
           if (cfd == -1) {
               perror("socket");
               return -1;
           }

           memset(&addr, 0, sizeof(addr));
           addr.sin_port = htons(connect_port);
           addr.sin_family = AF_INET;

           if (!inet_aton(address, (struct in_addr *) &addr.sin_addr.s_addr)) {
               perror("непÑавилÑнÑй ÑоÑÐ¼Ð°Ñ IP-адÑеÑа");
               close(cfd);
               return -1;
           }

           if (connect(cfd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
               perror("connect()");
               shutdown(cfd, SHUT_RDWR);
               close(cfd);
               return -1;
           }
           return cfd;
       }

       #define SHUT_FD1 do {                                \
                            if (fd1 >= 0) {                 \
                                shutdown(fd1, SHUT_RDWR);   \
                                close(fd1);                 \
                                fd1 = -1;                   \
                            }                               \
                        } while (0)

       #define SHUT_FD2 do {                                \
                            if (fd2 >= 0) {                 \
                                shutdown(fd2, SHUT_RDWR);   \
                                close(fd2);                 \
                                fd2 = -1;                   \
                            }                               \
                        } while (0)

       #define BUF_SIZE 1024

       int
       main(int argc, char *argv[])
       {
           int h;
           int fd1 = -1, fd2 = -1;
           char buf1[BUF_SIZE], buf2[BUF_SIZE];
           int buf1_avail = 0, buf1_written = 0;
           int buf2_avail = 0, buf2_written = 0;

           if (argc != 4) {
               fprintf(stderr, "ÐÑполÑзование\n\tfwd <пÑоÑлÑÑиваемÑй-поÑÑ> "
                        "<поÑÑ-пеÑенапÑавлениÑ> <ip-адÑеÑ-пеÑенапÑавлениÑ>\n");
               exit(EXIT_FAILURE);
           }

           signal(SIGPIPE, SIG_IGN);

           forward_port = atoi(argv[2]);

           h = listen_socket(atoi(argv[1]));
           if (h == -1)
               exit(EXIT_FAILURE);

           for (;;) {
               int ready, nfds = 0;
               ssize_t nbytes;
               fd_set readfds, writefds, exceptfds;

               FD_ZERO(&readfds);
               FD_ZERO(&writefds);
               FD_ZERO(&exceptfds);
               FD_SET(h, &readfds);
               nfds = max(nfds, h);

               if (fd1 > 0 && buf1_avail < BUF_SIZE)
                   FD_SET(fd1, &readfds);
                   /* ÐамеÑание: nfds обновлÑеÑÑÑ Ð½Ð¸Ð¶Ðµ, когда добавлÑеÑÑÑ fd1
                      в exceptfds. */
               if (fd2 > 0 && buf2_avail < BUF_SIZE)
                   FD_SET(fd2, &readfds);

               if (fd1 > 0 && buf2_avail - buf2_written > 0)
                   FD_SET(fd1, &writefds);
               if (fd2 > 0 && buf1_avail - buf1_written > 0)
                   FD_SET(fd2, &writefds);

               if (fd1 > 0) {
                   FD_SET(fd1, &exceptfds);
                   nfds = max(nfds, fd1);
               }
               if (fd2 > 0) {
                   FD_SET(fd2, &exceptfds);
                   nfds = max(nfds, fd2);
               }

               ready = select(nfds + 1, &readfds, &writefds, &exceptfds, NULL);

               if (ready == -1 && errno == EINTR)
                   continue;

               if (ready == -1) {
                   perror("select()");
                   exit(EXIT_FAILURE);
               }

               if (FD_ISSET(h, &readfds)) {
                   socklen_t addrlen;
                   struct sockaddr_in client_addr;
                   int fd;

                   addrlen = sizeof(client_addr);
                   memset(&client_addr, 0, addrlen);
                   fd = accept(h, (struct sockaddr *) &client_addr, &addrlen);
                   if (fd == -1) {
                       perror("accept()");
                   } else {
                       SHUT_FD1;
                       SHUT_FD2;
                       buf1_avail = buf1_written = 0;
                       buf2_avail = buf2_written = 0;
                       fd1 = fd;
                       fd2 = connect_socket(forward_port, argv[3]);
                       if (fd2 == -1)
                           SHUT_FD1;
                       else
                           printf("connect from %s\n",
                                   inet_ntoa(client_addr.sin_addr));

                       /* пÑопÑÑкаем вÑе ÑобÑÑÐ¸Ñ Ð´Ð»Ñ ÑÑаÑÑÑ, закÑÑÑÑÑ
                          ÑайловÑÑ Ð´ÐµÑкÑипÑоÑов */
                       continue;
                   }
               }

               /* ÐамеÑание: ÑÑение даннÑÑ OOB до обÑÑнÑÑ */

               if (fd1 > 0 && FD_ISSET(fd1, &exceptfds)) {
                   char c;

                   nbytes = recv(fd1, &c, 1, MSG_OOB);
                   if (nbytes < 1)
                       SHUT_FD1;
                   else
                       send(fd2, &c, 1, MSG_OOB);
               }
               if (fd2 > 0 && FD_ISSET(fd2, &exceptfds)) {
                   char c;

                   nbytes = recv(fd2, &c, 1, MSG_OOB);
                   if (nbytes < 1)
                       SHUT_FD2;
                   else
                       send(fd1, &c, 1, MSG_OOB);
               }
               if (fd1 > 0 && FD_ISSET(fd1, &readfds)) {
                   nbytes = read(fd1, buf1 + buf1_avail,
                             BUF_SIZE - buf1_avail);
                   if (nbytes < 1)
                       SHUT_FD1;
                   else
                       buf1_avail += nbytes;
               }
               if (fd2 > 0 && FD_ISSET(fd2, &readfds)) {
                   nbytes = read(fd2, buf2 + buf2_avail,
                             BUF_SIZE - buf2_avail);
                   if (nbytes < 1)
                       SHUT_FD2;
                   else
                       buf2_avail += nbytes;
               }
               if (fd1 > 0 && FD_ISSET(fd1, &writefds) && buf2_avail > 0) {
                   nbytes = write(fd1, buf2 + buf2_written,
                              buf2_avail - buf2_written);
                   if (nbytes < 1)
                       SHUT_FD1;
                   else
                       buf2_written += nbytes;
               }
               if (fd2 > 0 && FD_ISSET(fd2, &writefds) && buf1_avail > 0) {
                   nbytes = write(fd2, buf1 + buf1_written,
                              buf1_avail - buf1_written);
                   if (nbytes < 1)
                       SHUT_FD2;
                   else
                       buf1_written += nbytes;
               }

               /* ÐÑовеÑиÑÑ, ÑÑо запиÑаннÑе даннÑе бÑли пÑоÑиÑÐ°Ð½Ñ */

               if (buf1_written == buf1_avail)
                   buf1_written = buf1_avail = 0;
               if (buf2_written == buf2_avail)
                   buf2_written = buf2_avail = 0;

               /* Ðдна из ÑÑоÑон закÑÑла Ñоединение, пÑодолжаÑÑ
                   запиÑÑваÑÑ, пока дÑÑÐ³Ð°Ñ ÑÑоÑона не законÑÐ¸Ñ */

               if (fd1 < 0 && buf1_avail - buf1_written == 0)
                   SHUT_FD2;
               if (fd2 < 0 && buf2_avail - buf2_written == 0)
                   SHUT_FD1;
           }
           exit(EXIT_SUCCESS);
       }

       ÐÑÑепÑÐ¸Ð²ÐµÐ´ÐµÐ½Ð½Ð°Ñ Ð¿ÑогÑамма пÑавилÑно
       пеÑенапÑавлÑÐµÑ Ð±Ð¾Ð»ÑÑинÑÑво даннÑÑ Ð·Ð°Ð´Ð°Ñ,
       иÑполÑзÑÑÑÐ¸Ñ ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ TCP, вклÑÑаÑ
       внепоÑоÑнÑе (OOB) даннÑе, пеÑедаваемÑе
       ÑеÑвеÑами telnet. Ðна ÑпÑавлÑеÑÑÑ Ñо Ñложной
       пÑоблемой поддеÑÐ¶Ð°Ð½Ð¸Ñ Ð¾Ð´Ð½Ð¾Ð²Ñеменного
       двÑÑÑоÑоннего обмена даннÑми. Ðозможно, вÑ
       ÑеÑиÑе, ÑÑо ÑÑÑекÑивнее иÑполÑзоваÑÑ fork(2) и
       вÑделиÑÑ Ð¾ÑделÑнÑÑ Ð½Ð¸ÑÑ Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ поÑока.
       Ðа Ñамом деле ÑÑо Ñложнее, Ñем кажеÑÑÑ. ÐÑÑгой
       идеей Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¸ÑполÑзование
       неблокиÑÑÑÑего ввода-вÑвода Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ fcntl(2).
       ÐÑо Ñакже Ð¼Ð¾Ð¶ÐµÑ Ð²ÑзваÑÑ Ð¿ÑÐ¾Ð±Ð»ÐµÐ¼Ñ Ð¸Ð·-за Ñого,
       ÑÑо пÑидÑÑÑÑ Ð¸ÑполÑзоваÑÑ Ð½ÐµÑÑÑекÑивнÑе
       ÑаймаÑÑÑ.

       ÐÑогÑамма не обÑабаÑÑÐ²Ð°ÐµÑ Ð±Ð¾Ð»ÐµÐµ одного
       ÑоединениÑ, однако она Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð»ÐµÐ³ÐºÐ¾
       доÑабоÑана пÑÑем Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ ÑвÑзанного
       ÑпиÑка бÑÑеÑов â по Ð¾Ð´Ð½Ð¾Ð¼Ñ Ð½Ð° каждое
       Ñоединение. РданнÑй Ð¼Ð¾Ð¼ÐµÐ½Ñ Ð½Ð¾Ð²Ñе
       ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð¿ÑиводÑÑ Ðº закÑÑÑÐ¸Ñ ÑекÑÑего.

СÐÐТРÐТРТÐÐÐÐ
       accept(2), connect(2), ioctl(2), poll(2), read(2), recv(2), select(2),
       send(2), sigprocmask(2), write(2), sigaddset(3), sigdelset(3),
       sigemptyset(3), sigfillset(3), sigismember(3), epoll(7)



Linux                             2016-03-15                     SELECT_TUT(2)