Seth Woolley's Man Viewer

select_tut(2) - FD_CLR, FD_ISSET, FD_SET, FD_ZERO, pselect, select, FD_CLR, FD_ISSET, FD_SET, FD_ZERO, pselect, select - synchronous I/O multiplexing - man select_tut

([section] manual, -k keyword, -K [section] search, -f whatis)
man plain no title

SELECT_TUT(2)              Linux Programmer's Manual             SELECT_TUT(2)



NAME
       select(2,7,2 select_tut),  pselect,  FD_CLR,  FD_ISSET, FD_SET, FD_ZERO - synchronous I/O
       multiplexing

SYNOPSIS
       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

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

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

       FD_CLR(int fd, fd_set *set(7,n,1 builtins));
       FD_ISSET(int fd, fd_set *set(7,n,1 builtins));
       FD_SET(int fd, fd_set *set(7,n,1 builtins));
       FD_ZERO(fd_set *set(7,n,1 builtins));

DESCRIPTION
       select(2,7,2 select_tut) (or pselect) is the pivot function of most C programs that  han-
       dle more than one simultaneous file(1,n) descriptor (or socket(2,7,n) handle) in(1,8) an
       efficient manner. Its principal arguments  are  three  arrays  of  file(1,n)
       descriptors:  readfds,  writefds, and exceptfds. The way that select(2,7,2 select_tut) is
       usually used is to block while waiting for a "change of status" on  one
       or  more  of  the  file(1,n)  descriptors. A "change of status" is when more
       characters become available from the file(1,n)  descriptor,  or  when  space
       becomes  available  within the kernel's internal buffers for more to be
       written to the file(1,n) descriptor, or when a  file(1,n)  descriptor  goes  into
       error(8,n)  (in(1,8)  the  case of a socket(2,7,n) or pipe(2,8) this is when the other end of
       the connection is closed).

       In summary, select(2,7,2 select_tut) just watches multiple file(1,n) descriptors, and  is  the
       standard Unix call to do so.

       The  arrays  of file(1,n) descriptors are called file(1,n) descriptor sets.  Each
       set(7,n,1 builtins) is declared as type fd_set, and its contents can  be  altered  with
       the  macros  FD_CLR, FD_ISSET, FD_SET,  and FD_ZERO. FD_ZERO is usually
       the first function to be used on a newly declared set. Thereafter,  the
       individual file(1,n) descriptors that you are interested in(1,8) can be added one
       by one with FD_SET.  select(2,7,2 select_tut) modifies the contents of the sets according
       to the rules described below; after calling select(2,7,2 select_tut) you can test if(3,n) your
       file(1,n) descriptor is still present in(1,8) the set(7,n,1 builtins) with  the  FD_ISSET  macro.
       FD_ISSET  returns  non-zero if(3,n) the descriptor is present and zero if(3,n) it
       is not. FD_CLR removes a file(1,n) descriptor from the set(7,n,1 builtins) although I  can't
       see the use for it in(1,8) a clean program.


ARGUMENTS
       readfds
              This set(7,n,1 builtins) is watched to see if(3,n) data is available for reading from
              any of its file(1,n) descriptors. After select(2,7,2 select_tut) has returned,  readfds
              will  be  cleared  of all file(1,n) descriptors except for those file(1,n)
              descriptors that are immediately available for  reading  with  a
              recv()  (for  sockets) or read(2,n,1 builtins)() (for pipes, files, and sockets)
              call.

       writefds
              This set(7,n,1 builtins) is watched to see if(3,n) there is space to  write(1,2)  data  to
              any  of its file(1,n) descriptor. After select(2,7,2 select_tut) has returned, writefds
              will be cleared of all file(1,n) descriptors except  for  those  file(1,n)
              descriptors  that  are  immediately available for writing with a
              send(2,n)() (for sockets) or write(1,2)() (for pipes, files, and  sockets)
              call.

       exceptfds
              This  set(7,n,1 builtins) is watched for exceptions or errors on any of the file(1,n)
              descriptors. However, that is actually just a rumor. How you use
              exceptfds  is  to  watch for out-of-band (OOB) data. OOB data is
              data sent  on  a  socket(2,7,n)  using  the  MSG_OOB  flag,  and  hence
              exceptfds  only  really  applies  to  sockets.  See  recv(2) and
              send(2,n)(2) about this. After select(2,7,2 select_tut) has returned, exceptfds will be
              cleared  of  all  file(1,n)  descriptors except for those descriptors
              that are available for reading OOB data. You can only ever  read(2,n,1 builtins)
              one  byte  of  OOB  data though (which is done with recv()), and
              writing OOB data (done with send(2,n)) can be done at  any  time(1,2,n)  and
              will not block. Hence there is no need for a fourth set(7,n,1 builtins) to check
              if(3,n) a socket(2,7,n) is available for writing OOB data.

       nfds   This is an integer  one  more  than  the  maximum  of  any  file(1,n)
              descriptor  in(1,8)  any  of  the sets. In other words, while you are
              busy adding file(1,n) descriptors to your sets,  you  must  calculate
              the  maximum  integer  value of all of them, then increment this
              value by one, and then pass this as nfds to select(2,7,2 select_tut).

       utimeout
              This is the longest time(1,2,n) select(2,7,2 select_tut) must wait before returning, even
              if(3,n)  nothing  interesting  happened.  If  this value is passed as
              NULL, then select(2,7,2 select_tut) blocks  indefinitely  waiting  for  an  event.
              utimeout  can  be  set(7,n,1 builtins)  to  zero seconds, which causes select(2,7,2 select_tut) to
              return immediately. The structure struct timeval is defined as,

              struct timeval {
                  time_t tv_sec;    /* seconds */
                  long tv_usec;     /* microseconds */
              };

       ntimeout
              This argument has the same meaning as utimeout but struct  time-
              spec has nanosecond precision as follows,

              struct timespec {
                  long tv_sec;    /* seconds */
                  long tv_nsec;   /* nanoseconds */
              };

       sigmask
              This argument holds a set(7,n,1 builtins) of signals to allow while performing a
              pselect call (see sigaddset(3) and sigprocmask(2)).  It  can  be
              passed  as  NULL,  in(1,8)  which  case it does not modify the set(7,n,1 builtins) of
              allowed signals on entry and exit(3,n,1 builtins) to the function. It will  then
              behave just like select(2,7,2 select_tut).


COMBINING SIGNAL AND DATA EVENTS
       pselect  must  be  used if(3,n) you are waiting for a signal(2,7) as well as data
       from a file(1,n) descriptor. Programs that receive signals  as  events  nor-
       mally  use  the  signal(2,7) handler only to raise(3,n) a global flag. The global
       flag will indicate that the event must be processed in(1,8) the main loop of
       the program. A signal(2,7) will cause the select(2,7,2 select_tut) (or pselect) call to return
       with errno set(7,n,1 builtins) to EINTR. This behavior is essential so that signals can
       be  processed  in(1,8)  the main loop of the program, otherwise select(2,7,2 select_tut) would
       block indefinitely. Now, somewhere in(1,8) the main loop will  be  a  condi-
       tional  to  check  the  global  flag.  So we must ask: what if(3,n) a signal(2,7)
       arrives after the conditional, but before the select(2,7,2 select_tut) call?  The  answer
       is  that select(2,7,2 select_tut) would block indefinitely, even though an event is actu-
       ally pending. This race condition is solved by the pselect  call.  This
       call can be used to mask out signals that are not to be received except
       within the pselect call. For instance, let us say  that  the  event  in(1,8)
       question  was the exit(3,n,1 builtins) of a child process. Before the start of the main
       loop, we would block SIGCHLD using sigprocmask. Our pselect call  would
       enable  SIGCHLD by using the virgin signal(2,7) mask. Our program would look(1,8,3 Search::Dict)
       like:

       int child_events = 0;

       void child_sig_handler (int x) {
           child_events++;
           signal(2,7) (SIGCHLD, child_sig_handler);
       }

       int main (int argc, char **argv) {
           sigset_t sigmask, orig_sigmask;

           sigemptyset (&sigmask);
           sigaddset (&sigmask, SIGCHLD);
           sigprocmask (SIG_BLOCK, &sigmask,
                                       &orig_sigmask);

           signal(2,7) (SIGCHLD, child_sig_handler);

           for (;;) { /* main loop */
               for (; child_events > 0; child_events--) {
                   /* do event work here */
               }
               r = pselect (nfds, &rd, &wr, &er, 0, &orig_sigmask);

               /* main body of program */
           }
       }

       Note that the above pselect call can be replaced with:

               sigprocmask (SIG_BLOCK, &orig_sigmask, 0);
               r = select(2,7,2 select_tut) (nfds, &rd, &wr, &er, 0);
               sigprocmask (SIG_BLOCK, &sigmask, 0);

       but then there is still the possibility  that  a  signal(2,7)  could  arrive
       after  the  first sigprocmask and before the select(2,7,2 select_tut). If you do do this,
       it is prudent to at least put a finite timeout(1,3x,3x cbreak) so that the process does
       not  block. At present glibc probably works this way.  The Linux kernel
       does not have a native pselect system call as yet so this is all proba-
       bly much of a moot point.




PRACTICAL
       So  what  is  the  point  of  select(2,7,2 select_tut)? Can't I just read(2,n,1 builtins) and write(1,2) to my
       descriptors whenever I want? The point of select(2,7,2 select_tut)  is  that  it  watches
       multiple  descriptors at the same time(1,2,n) and properly puts(3,n) the process to
       sleep(1,3) if(3,n) there is no activity. It does this while enabling you to  han-
       dle  multiple  simultaneous  pipes  and sockets. Unix programmers often
       find themselves in(1,8) a position where they have to handle  IO  from  more
       than  one  file(1,n)  descriptor where the data flow may be intermittent. If
       you were to merely create a sequence of read(2,n,1 builtins) and write(1,2) calls, you would
       find  that  one of your calls may block waiting for data from/to a file(1,n)
       descriptor, while another file(1,n) descriptor is  unused  though  available
       for data. select(2,7,2 select_tut) efficiently copes with this situation.

       A classic example of select(2,7,2 select_tut) comes from the select(2,7,2 select_tut) man(1,5,7) page:

       #include <stdio.h>
       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

       int
       main(void) {
           fd_set rfds;
           struct timeval tv;
           int retval;

           /* Watch stdin (fd 0) to see when it has input. */
           FD_ZERO(&rfds);
           FD_SET(0, &rfds);
           /* Wait up to five seconds. */
           tv.tv_sec = 5;
           tv.tv_usec = 0;

           retval = select(2,7,2 select_tut)(1, &rfds, NULL, NULL, &tv);
           /* Don't rely on the value of tv now! */

           if(3,n) (retval == -1)
               perror(1,3)("select(2,7,2 select_tut)()");
           else if(3,n) (retval)
               printf(1,3,1 builtins)("Data is available now.\n");
               /* FD_ISSET(0, &rfds) will be true. */
           else
               printf(1,3,1 builtins)("No data within five seconds.\n");

           exit(3,n,1 builtins)(0);
       }



PORT FORWARDING EXAMPLE
       Here is an example that better demonstrates the true utility of select(2,7,2 select_tut).
       The listing below a TCP forwarding program that forwards from  one  TCP
       port to another.

       #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 a;
           int s;
           int yes;
           if(3,n) ((s = socket(2,7,n) (AF_INET, SOCK_STREAM, 0)) < 0) {
               perror(1,3) ("socket(2,7,n)");
               return -1;
           }
           yes = 1;
           if(3,n) (setsockopt
               (s, SOL_SOCKET, SO_REUSEADDR,
                (char *) &yes, sizeof (yes)) < 0) {
               perror(1,3) ("setsockopt");
               close(2,7,n) (s);
               return -1;
           }
           memset (&a, 0, sizeof (a));
           a.sin_port = htons (listen_port);
           a.sin_family = AF_INET;
           if(3,n) (bind(2,n,