Waiting for Input or Output
===========================
Sometimes a program needs to accept input on multiple input channels
whenever input arrives. For example, some workstations may have devices
such as a digitizing tablet, function button box, or dial box that are
connected via normal asynchronous serial interfaces; good user interface
style requires responding immediately to input on any device. Another
example is a program that acts as a server to several other processes
via pipes or sockets.
You cannot normally use `read' for this purpose, because this blocks
the program until input is available on one particular file descriptor;
input on other channels won't wake it up. You could set nonblocking
mode and poll each file descriptor in turn, but this is very
inefficient.
A better solution is to use the `select' function. This blocks the
program until input or output is ready on a specified set of file
descriptors, or until a timer expires, whichever comes first. This
facility is declared in the header file `sys/types.h'.
In the case of a server socket (Note:Listening), we say that
"input" is available when there are pending connections that could be
accepted (Note:Accepting Connections). `accept' for server sockets
blocks and interacts with `select' just as `read' does for normal input.
The file descriptor sets for the `select' function are specified as
`fd_set' objects. Here is the description of the data type and some
macros for manipulating these objects.
- Data Type: fd_set
The `fd_set' data type represents file descriptor sets for the
`select' function. It is actually a bit array.
- Macro: int FD_SETSIZE
The value of this macro is the maximum number of file descriptors
that a `fd_set' object can hold information about. On systems
with a fixed maximum number, `FD_SETSIZE' is at least that number.
On some systems, including GNU, there is no absolute limit on the
number of descriptors open, but this macro still has a constant
value which controls the number of bits in an `fd_set'; if you get
a file descriptor with a value as high as `FD_SETSIZE', you cannot
put that descriptor into an `fd_set'.
- Macro: void FD_ZERO (fd_set *SET)
This macro initializes the file descriptor set SET to be the empty
set.
- Macro: void FD_SET (int FILEDES, fd_set *SET)
This macro adds FILEDES to the file descriptor set SET.
- Macro: void FD_CLR (int FILEDES, fd_set *SET)
This macro removes FILEDES from the file descriptor set SET.
- Macro: int FD_ISSET (int FILEDES, fd_set *SET)
This macro returns a nonzero value (true) if FILEDES is a member
of the file descriptor set SET, and zero (false) otherwise.
Next, here is the description of the `select' function itself.
- Function: int select (int NFDS, fd_set *READ-FDS, fd_set *WRITE-FDS,
fd_set *EXCEPT-FDS, struct timeval *TIMEOUT)
The `select' function blocks the calling process until there is
activity on any of the specified sets of file descriptors, or
until the timeout period has expired.
The file descriptors specified by the READ-FDS argument are
checked to see if they are ready for reading; the WRITE-FDS file
descriptors are checked to see if they are ready for writing; and
the EXCEPT-FDS file descriptors are checked for exceptional
conditions. You can pass a null pointer for any of these
arguments if you are not interested in checking for that kind of
condition.
A file descriptor is considered ready for reading if it is not at
end of file. A server socket is considered ready for reading if
there is a pending connection which can be accepted with `accept';
Note:Accepting Connections. A client socket is ready for
writing when its connection is fully established; Note:Connecting.
"Exceptional conditions" does not mean errors--errors are reported
immediately when an erroneous system call is executed, and do not
constitute a state of the descriptor. Rather, they include
conditions such as the presence of an urgent message on a socket.
(Note:Sockets, for information on urgent messages.)
The `select' function checks only the first NFDS file descriptors.
The usual thing is to pass `FD_SETSIZE' as the value of this
argument.
The TIMEOUT specifies the maximum time to wait. If you pass a
null pointer for this argument, it means to block indefinitely
until one of the file descriptors is ready. Otherwise, you should
provide the time in `struct timeval' format; see Note:High-Resolution Calendar. Specify zero as the time (a `struct
timeval' containing all zeros) if you want to find out which
descriptors are ready without waiting if none are ready.
The normal return value from `select' is the total number of ready
file descriptors in all of the sets. Each of the argument sets is
overwritten with information about the descriptors that are ready
for the corresponding operation. Thus, to see if a particular
descriptor DESC has input, use `FD_ISSET (DESC, READ-FDS)' after
`select' returns.
If `select' returns because the timeout period expires, it returns
a value of zero.
Any signal will cause `select' to return immediately. So if your
program uses signals, you can't rely on `select' to keep waiting
for the full time specified. If you want to be sure of waiting
for a particular amount of time, you must check for `EINTR' and
repeat the `select' with a newly calculated timeout based on the
current time. See the example below. See also Note:Interrupted
Primitives.
If an error occurs, `select' returns `-1' and does not modify the
argument file descriptor sets. The following `errno' error
conditions are defined for this function:
`EBADF'
One of the file descriptor sets specified an invalid file
descriptor.
`EINTR'
The operation was interrupted by a signal. Note:Interrupted
Primitives.
`EINVAL'
The TIMEOUT argument is invalid; one of the components is
negative or too large.
*Portability Note:* The `select' function is a BSD Unix feature.
Here is an example showing how you can use `select' to establish a
timeout period for reading from a file descriptor. The `input_timeout'
function blocks the calling process until input is available on the
file descriptor, or until the timeout period expires.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
int
input_timeout (int filedes, unsigned int seconds)
{
fd_set set;
struct timeval timeout;
/* Initialize the file descriptor set. */
FD_ZERO (&set);
FD_SET (filedes, &set);
/* Initialize the timeout data structure. */
timeout.tv_sec = seconds;
timeout.tv_usec = 0;
/* `select' returns 0 if timeout, 1 if input available, -1 if error. */
return TEMP_FAILURE_RETRY (select (FD_SETSIZE,
&set, NULL, NULL,
&timeout));
}
int
main (void)
{
fprintf (stderr, "select returned %d.\n",
input_timeout (STDIN_FILENO, 5));
return 0;
}
There is another example showing the use of `select' to multiplex
input from multiple sockets in Note:Server Example.