Sunday, November 9, 2014

Unix Prog: I/O Multiplexing(2)

1. fd_set
fd_set is a data structure used to store a set of file descriptors.
A few functions are defined to manipulate the fd_set:

 ubuntu@ip-172-31-23-227:~$ less /usr/include/x86_64-linux-gnu/sys/select.h  
 ......  
 /* Access macros for `fd_set'. */  
 #define FD_SET(fd, fdsetp)   __FD_SET (fd, fdsetp)  
 #define FD_CLR(fd, fdsetp)   __FD_CLR (fd, fdsetp)  
 #define FD_ISSET(fd, fdsetp)  __FD_ISSET (fd, fdsetp)  
 #define FD_ZERO(fdsetp)     __FD_ZERO (fdsetp)  
 ......  

2. pselect
pselect is identical to select but provides a finer control.
1) The timeout value for "select" is specified by a timeval structure, but for pselect , a timespec structure is used, which provides nanosecond level time control.
2) The timeout value for "pselect" is declared const, and its value will not change as a result of calling pselect.
3) signal mask provide a way to install the signal mask atomically while calling the pselect, on return, the previous signal is restored.

3. Example:
select.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<sys/select.h>  
 #include<unistd.h>  
 #include<fcntl.h>  
   
 int main(int argc, char* argv[])  
 {  
  int fd1;  
  int fd2;  
   
  // Open file descriptors  
  if((fd1 = open(argv[1], O_RDONLY)) < 0) {  
   printf("open error!\n");  
   exit(1);  
  }  
   
  if((fd2 = open(argv[2], O_RDWR | O_CREAT | O_TRUNC)) < 0) {  
   printf("open error!\n");  
   exit(1);  
  }  
   
  // Setup select  
  int maxfd = (fd1 > fd2)? fd1 : fd2;  
  struct timeval ts;  
  ts.tv_sec = 2;  
  ts.tv_usec = 500;  
   
  fd_set rset, wset, eset;  
  FD_ZERO(&rset);  
  FD_ZERO(&wset);  
  FD_ZERO(&eset);  
  FD_SET(fd1, &rset);  
  FD_SET(fd2, &rset);  
  FD_SET(fd1, &wset);  
  FD_SET(fd2, &wset);  
   
  int rc;  
  if((rc = select(maxfd+1, &rset, &wset, &eset, &ts)) == 0) {  
   printf("time out, no fd is ready.\n");  
   exit(2);  
  } else if(rc < 0) {  
   printf("sth wrong happens.\n");  
   exit(3);  
  }  
   
  // Output ready descriptors  
  printf("there are totally %d file descriptors are ready.\n", rc);  
   
  if(FD_ISSET(fd1, &rset)) {  
   printf("fd1 is ready for read.\n");  
  }  
  if(FD_ISSET(fd2, &rset)) {  
   printf("fd2 is ready for read.\n");  
  }  
  if(FD_ISSET(fd1, &wset)) {  
   printf("fd1 is ready for write.\n");  
  }  
  if(FD_ISSET(fd2, &wset)) {  
   printf("fd2 is ready for write.\n");  
  }  
  close(fd1);  
  close(fd2);  
  exit(0);  
 }  

shell:
 ubuntu@ip-172-31-23-227:~$ ./select.out test1 test2  
 there are totally 4 file descriptors are ready.  
 fd1 is ready for read.  
 fd2 is ready for read.  
 fd1 is ready for write.  
 fd2 is ready for write.  

4. poll function
poll function is similar from select, just the interface is different.
System Definition:
 ubuntu@ip-172-31-23-227:~$ less /usr/include/x86_64-linux-gnu/sys/poll.h  
 ......  
 /* Data structure describing a polling request. */  
 struct pollfd  
  {  
   int fd;           /* File descriptor to poll. */  
   short int events;      /* Types of events poller cares about. */  
   short int revents;     /* Types of events that actually occurred. */  
  };  
   
   
 __BEGIN_DECLS  
   
 /* Poll the file descriptors described by the NFDS structures starting at  
   FDS. If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for  
   an event to occur; if TIMEOUT is -1, block until an event occurs.  
   Returns the number of file descriptors with events, zero if timed out,  
   or -1 for errors.  
   
   This function is a cancellation point and therefore not marked with  
   __THROW. */  
 extern int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout);  
 ......  

Every time we call poll, we need to create an array of struct pollfd, including the file descriptor, event type we care about. When poll function returns, fd, events don't change, but revents is repopulated to tell which event occured. Following is the list of all events:
 ubuntu@ip-172-31-23-227:~$ less /usr/include/x86_64-linux-gnu/bits/poll.h  
 ......  
 /* Event types that can be polled for. These bits may be set in `events'  
   to indicate the interesting event types; they will appear in `revents'  
   to indicate the status of the file descriptor. */  
 #define POLLIN     0x001      /* There is data to read. */  
 #define POLLPRI     0x002      /* There is urgent data to read. */  
 #define POLLOUT     0x004      /* Writing now will not block. */  
   
 #if defined __USE_XOPEN || defined __USE_XOPEN2K8  
 /* These values are defined in XPG4.2. */  
 # define POLLRDNORM   0x040      /* Normal data may be read. */  
 # define POLLRDBAND   0x080      /* Priority data may be read. */  
 # define POLLWRNORM   0x100      /* Writing now will not block. */  
 # define POLLWRBAND   0x200      /* Priority data may be written. */  
 #endif  
   
 #ifdef __USE_GNU  
 /* These are extensions for Linux. */  
 # define POLLMSG    0x400  
 # define POLLREMOVE   0x1000  
 # define POLLRDHUP   0x2000  
 #endif  
   
 /* Event types always implicitly polled for. These bits need not be set in  
   `events', but they will appear in `revents' to indicate the status of  
   the file descriptor. */  
 #define POLLERR     0x008      /* Error condition. */  
 #define POLLHUP     0x010      /* Hung up. */  
 #define POLLNVAL    0x020      /* Invalid polling request. */  
 ......  

Note: It is important to realize the difference between an end of file and a hangup. If we're entering data from the terminal and type the end-of-file character, POLLIN is turned on. If we're reading from a modem and the telephone line is hungup, we'll receive the POLLHUP notification.

For select and poll, whether the file descriptor is blocking or not doesn't affect the select and poll's blocking.

No comments:

Post a Comment