6.9 pselect
Function
The pselect function was invented by
POSIX and is now supported by many of the Unix variants.
#include <sys/select.h>
|
#include <signal.h>
|
#include <time.h>
|
int pselect (int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timespec
*timeout, const sigset_t
*sigmask);
|
Returns: count of ready descriptors, 0 on
timeout, 鈥? on error
|
pselect contains two changes from the
normal select function:
-
pselect
uses the timespec structure, another POSIX invention,
instead of the timeval structure.
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
The difference in these two structures is with
the second member: The tv_nsec member of the newer
structure specifies nanoseconds, whereas the tv_usec
member of the older structure specifies microseconds.
-
pselect adds a sixth argument: a
pointer to a signal mask. This allows the program to disable the
delivery of certain signals, test some global variables that are
set by the handlers for these now-disabled signals, and then call
pselect, telling it to reset the signal mask.
With regard to the second point, consider the
following example (discussed on pp. 308鈥?09 of APUE). Our program's
signal handler for SIGINT just sets the global
intr_flag and returns. If our process is blocked in a call
to select, the return from the signal handler causes the
function to return with errno set to EINTR. But
when select is called, the code looks like the
following:
if (intr_flag)
handle_intr(); /* handle the signal */
if ( (nready = select( ... )) < 0) {
if (errno == EINTR) {
if (intr_flag)
handle_intr();
}
...
}
The problem is that between the test of
intr_flag and the call to select, if the signal
occurs, it will be lost if select blocks forever. With
pselect, we can now code this example reliably as
sigset_t newmask, oldmask, zeromask;
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);
sigprocmask(SIG_BLOCK, &newmask, &oldmask); /* block SIGINT */
if (intr_flag)
handle_intr(); /* handle the signal */
if ( (nready = pselect ( ... , &zeromask)) < 0) {
if (errno == EINTR) {
if (intr_flag)
handle_intr ();
}
...
}
Before testing the intr_flag variable,
we block SIGINT. When pselect is called, it
replaces the signal mask of the process with an empty set (i.e.,
zeromask) and then checks the descriptors, possibly going
to sleep. But when pselect returns, the signal mask of the
process is reset to its value before pselect was called
(i.e., SIGINT is blocked).
We will say more about pselect and show
an example of it in Section 20.5. We
will use pselect in Figure 20.7 and show a
simple, albeit incorrect, implementation of pselect in
Figure 20.8.
There is one other slight difference between the
two select functions. The first member of the
timeval structure is a signed long integer, while the
first member of the timespec structure is a
time_t. The signed long in the former should also be a
time_t, but was not changed retroactively to avoid
breaking existing code. The brand new function, however, could make
this change.
|