File Handling in Linux 1


2. General File handling (including pipes and sockets)

2.1 How to manage multiple connections?

I have to monitor more than one (fd/connection/stream) at a time. How do I manage all of them?
Use select() or poll().
Note: select() was introduced in BSD, whereas poll() is an artifact of SysV STREAMS. As such, there are portability issues; pure BSD systems may still lack poll(), whereas some older SVR3 systems may not have select(). SVR4 added select(), and the Posix.1g standard defines both.
select() and poll() essentially do the same thing, just differently. Both of them examine a set of file descriptors to see if specific events are pending on any, and then optionally wait for a specified time for an event to happen.
[Important note: neither select() nor poll() do anything useful when applied to plain files; they are useful for sockets, pipes, ptys, ttys & possibly other character devices, but this is system-dependent.]
There the similarity ends....

2.1.1 How do I use select()?

The interface to select() is primarily based on the concept of an fd_set, which is a set of FDs (usually implemented as a bit-vector). In times past, it was common to assume that FDs were smaller than 32, and just use an int to store the set, but these days, one usually has more FDs available, so it is important to use the standard macros for manipulating fd_sets:
fd_set set;
FD_ZERO(&set);      /* empties the set */
FD_SET(fd,&set);    /* adds FD to the set */
FD_CLR(fd,&set);    /* removes FD from the set */
FD_ISSET(fd,&set)   /* true if FD is in the set */
In most cases, it is the system's responsibility to ensure that fdsets can handle the whole range of file descriptors, but in some cases you may have to predefine the FD_SETSIZE macro. This is system-dependent; check your select() manpage. Also, some systems have problems handling more than 1024 file descriptors in select().
The basic interface to select is simple:
int select(int nfds, fd_set *readset, 
                     fd_set *writeset,
                     fd_set *exceptset, struct timeval *timeout);
where
nfds
the number of FDs to examine; this must be greater than the largest FD in any of the fdsets, not the actual number of FDs specified
readset
the set of FDs to examine for readability
writeset
the set of FDs to examine for writability
exceptfds
the set of FDs to examine for exceptional status (note: errors are not exceptional statuses)
timeout
NULL for infinite timeout, or points to a timeval specifying the maximum wait time (if tv_sec and tv_usec both equal zero, then the status of the FDs is polled, but the call never blocks)
The call returns the number of `ready' FDs found, and the three fdsets are modified in-place, with only the ready FDs left in the sets. Use the FD_ISSET macro to test the returned sets.
Here's a simple example of testing a single FD for readability:
int isready(int fd)
{
    int rc;
    fd_set fds;
    struct timeval tv;
 
    FD_ZERO(&fds);
    FD_SET(fd,&fds);
    tv.tv_sec = tv.tv_usec = 0;
 
    rc = select(fd+1, &fds, NULL, NULL, &tv);
    if (rc < 0)
      return -1;
 
    return FD_ISSET(fd,&fds) ? 1 : 0;
}
Note that we can pass NULL for fdsets that we aren't interested in testing.

2.1.2 How do I use poll()?

poll() accepts a pointer to a list of struct pollfd, in which the descriptors and the events you wish to poll for are stored. The events are specified via a bitwise mask in the events field of the structure. The instance of the structure will later be filled in and returned to you with any events which occured. Macros defined by `poll.h' on SVR4 (probably older versions as well), are used to specify the events in the field. A timeout may be specified in milliseconds, only the type provided is an integer which is quite perplexing. A timeout of 0 causes poll() to return immediately; a value of @math{-1} will suspend poll until an event is found to be true.
struct pollfd {
    int fd;        /* The descriptor. */
    short events;  /* The event(s) is/are specified here. */
   short revents; /* Events found are returned here. */
};
A lot like select(), the return value if positive reflects how many descriptors were found to satisfy the events requested. A zero return value is returned if the timeout period is reached before any of the events specified have occured. A negative value should immediately be followed by a check of errno, since it signifies an error.
If no events are found, revents is cleared, so there's no need for you to do this yourself.
The returned events are tested to contain the event.
Here's an example:
/* Poll on two descriptors for Normal data, or High priority data.
   If any found call function handle() with appropriate descriptor
   and priority. Don't timeout, only give up if error, or one of the
   descriptors hangs up. */
 
#include <stdlib.h>
#include <stdio.h>
 
#include <sys/types.h>
#include <stropts.h>
#include <poll.h>
 
#include <unistd.h>
#include <errno.h>
#include <string.h>
 
#define NORMAL_DATA 1
#define HIPRI_DATA 2
 
int poll_two_normal(int fd1,int fd2)
{
    struct pollfd poll_list[2];
    int retval;
 
    poll_list[0].fd = fd1;
    poll_list[1].fd = fd2;
    poll_list[0].events = POLLIN|POLLPRI;
    poll_list[1].events = POLLIN|POLLPRI;
 
    while(1)
    {
        retval = poll(poll_list,(unsigned long)2,-1);
        /* Retval will always be greater than 0 or -1 in this case.
           Since we're doing it while blocking */
 
        if(retval < 0)
        {
            fprintf(stderr,"Error while polling: %s\n",strerror(errno));
            return -1;
        }
 
        if(((poll_list[0].revents&POLLHUP) == POLLHUP) ||
           ((poll_list[0].revents&POLLERR) == POLLERR) ||
           ((poll_list[0].revents&POLLNVAL) == POLLNVAL) ||
           ((poll_list[1].revents&POLLHUP) == POLLHUP) ||
           ((poll_list[1].revents&POLLERR) == POLLERR) ||
           ((poll_list[1].revents&POLLNVAL) == POLLNVAL)) 
          return 0;
 
        if((poll_list[0].revents&POLLIN) == POLLIN)
          handle(poll_list[0].fd,NORMAL_DATA);
        if((poll_list[0].revents&POLLPRI) == POLLPRI)
          handle(poll_list[0].fd,HIPRI_DATA);
        if((poll_list[1].revents&POLLIN) == POLLIN)
          handle(poll_list[1].fd,NORMAL_DATA);
        if((poll_list[1].revents&POLLPRI) == POLLPRI)
          handle(poll_list[1].fd,HIPRI_DATA);
    }
}


To Look for similar posts on File handling in Linux explore the following links from the same blog as well.
 
 
 

Post a Comment

Previous Post Next Post