Overview
- 使用 select 监听文件描述符/套接字是否有 ready,如是否有连接请求,是
否有数据可读;
- fdsets 将文件描述符打包供 select 监听,从而实现了对多个文件描述符的
同时监听;
- select 返回后,只有处于 ready 状态的文件描述符在 fdsets 中保留,其
它的都被清空,可使用 FD_ISSET 函数判断某个描述符是否 ready;
- 对于套接字编成,使用 select 监听,有连接请求时调 accept 接受请求并
建立连接,不要用 accept 监听。
Synopsis
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *utimeout);
FD_CLR(int fd, fd_set *set);
FD_ISSET(int fd, fd_set *set);
FD_SET(int fd, fd_set *set);
FD_ZERO(fd_set *set);
Description
select() is the pivot function of most C programs that handle more
than one simultaneous file descriptor (or socket handle) in an
efficient manner. Its principal arguments are three arrays of file
descriptors: readfds, writefds, and exceptfds. The way that select()
is usually used is to block while waiting for a "change of status" on
one or more of the file descriptors. A "change of status" is when
more characters become available from the file descriptor, or when
space becomes available within the kernel’s internal buffers for
more to be written to the file descriptor, or when a file descriptor
goes into error (in the case of a socket or pipe this is when the
other end of the connection is closed).
In summary, select() just watches multiple file descriptors, and is
the standard Unix call to do so.
The arrays of file descriptors are called file descriptor sets. Each
set 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.
There-after, the individual file descriptors that you are interested
in can be added one by one with FD_SET(). select() modifies the
contents of the sets according to the rules described below; after
calling select() you can test if your file descriptor is still present
in the set with the FD_ISSET() macro. FD_ISSET() returns non-zero
if the descriptor is present and zero if it is not. FD_CLR() removes a
file descriptor from the set.
Select Law
Many people who try to use select() come across behavior that is
difficult to understand and produces non-portable or borderline
results. For instance, the above program is carefully written not
to block at any point, even though it does not set its file
descriptors to non-blocking mode at all (using ioctl). It is easy to
introduce subtle errors that will remove the advantage of using
select(), hence I will present a list of essentials to watch for
when using the select() call.
- You should always try to use select() without a timeout. Your
program should have nothing to do if there is no data available. Code
that depends on timeouts is not usually portable and is difficult
to debug.
- The value nfds must be properly calculated for efficiency as
explained above.
- No file descriptor must be added to any set if you do not intend
to check its result after the select() call, and respond
appropriately. See next rule.
- After select() returns, all file descriptors in all sets should
be checked to see if they are ready.
- The functions read(), recv(), write(), and send() do not
necessarily read/write the full amount of data that you have
requested. If they do read/write the full amount, its because you
have a low traffic load and a fast stream. This is not always
going to be the case. You should cope with the case of your
functions only managing to send or receive a single byte.
- Never read/write only in single bytes at a time unless your are
really sure that you have a small amount of data to process. It is
extremely inefficient not to read/write as much data as you can
buffer each time. The buffers in the example above are 1024 bytes
although they could easily be made larger.
- The functions read(), recv(), write(), and send() as well as the
select() call can return -1 with errno set to EINTR, or with errno
set to EAGAIN (EWOULDBLOCK). These results must be properly managed
(not done properly above). If your program is not going to receive
any signals then it is unlikely you will get EINTR. If your program
does not set non-blocking I/O, you will not get EAGAIN.
Nonetheless you should still cope with these errors for completeness.
- Never call read(), recv(), write(), or send() with a buffer length of zero.
- If the functions read(), recv(), write(), and send() fail with
errors other than those listed in 7., or one of the input functions
returns 0, indicating end of file, then you should not pass that
descriptor to select() again. In the above example, I close the
descriptor immediately, and then set it to -1 to prevent it being included in a set.
- The timeout value must be initialized with each new call to
select(), since some operating systems modify the structure. pselect() however does not modify its timeout structure.
- I have heard that the Windows socket layer does not cope with
OOB data properly. It also does not cope with select() calls when
no file descriptors are set at all. Having no file descriptors set
is a useful way to sleep the process with sub-second precision by using the timeout.
Notes
Generally speaking, all operating systems that support sockets,
also support select(). Many types of programs become extremely
complicated without the use of select(). select() can be used to
solve many problems in a portable and efficient way that naive
programmers try to solve in a more complicated manner using threads,
forking, IPCs, signals, memory sharing, and so on.
Practical
There is a PORT FORWARDING EXAMPLE in "man select_tut".
SeeAlso
- man 2 select
- man select_tut
|