函数
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
int pselect(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, const struct timespec *timeout,
const sigset_t *sigmask);
描述
select() and pselect() allow a program to monitor multiple file descriptors, waiting until one or more of the file descriptors become “ready” for some class of I/O operation (e.g., input possible).
A file descriptor is considered ready if it is possible to perform the corresponding I/O operation (e.g., read(2)) without blocking.
select或pselect会持续检测多个文件描述符,直到其中一个或多个文件描述符对于某些IO操作准备好了。
当一个文件描述符可以无阻塞的执行文件操作时即认为这个文件描述符准备好了。
The operation of select() and pselect() is identical, with three differences:
(i) select() uses a timeout that is a struct timeval (with seconds and microseconds), while pselect() uses a struct timespec (with seconds and nanoseconds).
(ii) select() may update the timeout argument to indicate how much time was left. pselect() does not change this argument.
(iii) select() has no sigmask argument, and behaves as pselect() called with NULL sigmask.
select和pselect有如下三个区别:
(1) select() timeout 用的是timeval结构体(里面有秒和微秒),pselect用的是timespec结构体(里面有秒和纳秒)
(2)select timeout是个输入输出参数,表示还剩余多少时间,pselect timeout是const,不能变
(3)pselect相比select多了个sigmask参数
Three independent sets of file descriptors are watched. Those listed in readfds will be watched to see if characters become available for reading (more precisely, to see if a read will not block; in particular, a file descriptor is also ready on end-of-file), those in writefds will be watched to see if a write will not block, and those in exceptfds will be watched for exceptions. On exit,
the sets are modified in place to indicate which file descriptors actually changed status. Each of the three file descriptor sets may be specified as NULL if no file descriptors are to be watched
for the corresponding class of events.
共有三个独立的文件描述符集可以监视文件状态的变化,分别对应着如下三个参数。
readfds,可以监视文件可读状态的变化(更精确的说,是无阻塞可读,特别地,在EOF处也是可读的);
writefds,可以监视文件可写状态的变化
exceptfds,可以监视异常
这三个文件描述符集会在文件状态改变时立即发生改变。
把对应的参数置为NULL,表示不监视对应的类型的文件状态的改变
Four macros are provided to manipulate the sets. FD_ZERO() clears a set. FD_SET() and FD_CLR() respectively add and remove a given file descriptor from a set. FD_ISSET() tests to see if a file descriptor is part of the set; this is useful after select() returns.
共有四个宏去操作上面的文件描述符集(以下简称集)。
FD_ZERO()用来清除一个集,
FD_SET()用来把给定的文件描述符添加到集。
FD_CLR()用来把给定的文件描述符从集中移除
FD_ISSET()用来返回文件描述符是否在集中,一般在select返回后进行判断。
nfds is the highest-numbered file descriptor in any of the three sets, plus 1.
nfds参数是三种集中最大的文件描述符数量加1
timeout is an upper bound on the amount of time elapsed before select() returns. If both fields of the timeval structure are zero, then select() returns immediately. (This is useful for polling.)
If timeout is NULL (no timeout), select() can block indefinitely.
timeout参数是select()返回前的等待的时间上限。如果时间上限是0,select会立即返回(这对polling是有用的)。如果timeout参数是NULL, select会阻塞。
sigmask is a pointer to a signal mask (see sigprocmask(2)); if it is not NULL, then pselect() first replaces the current signal mask by the one pointed to by sigmask, then does the “select” function, and then restores the original signal mask.
Other than the difference in the precision of the timeout argument, the following pselect()
ready = pselect(nfds, &readfds, &writefds, &exceptfds, timeout, &sigmask);
is equivalent to atomically executing the following calls:
sigset_t origmask;
sigprocmask(SIG_SETMASK, &sigmask, &origmask);
ready = select(nfds, &readfds, &writefds, &exceptfds, timeout);
sigprocmask(SIG_SETMASK, &origmask, NULL);
sigmask 是一个指向signal mask 的指针,如果它不为NULL,pselect() 首先把当前的signal mask更换成sigmask 指向的signal mask,之后做和select() 一样的事情,最后回复最初的signal mask。
用代码解释如下:
ready = pselect(nfds, &readfds, &writefds, &exceptfds,timeout, &sigmask);
等驾于
sigset_t origmask;
sigprocmask(SIG_SETMASK, &sigmask, &origmask);
ready = select(nfds, &readfds, &writefds, &exceptfds, timeout);
sigprocmask(SIG_SETMASK, &origmask, NULL);
The reason that pselect() is needed is that if one wants to wait for either a signal or for a file descriptor to become ready, then an atomic test is needed to prevent race conditions. (Suppose the
signal handler sets a global flag and returns. Then a test of this global flag followed by a call of select() could hang indefinitely if the signal arrived just after the test but just before the
call. By contrast, pselect() allows one to first block signals, handle the signals that have come in, then call pselect() with the desired sigmask, avoiding the race.)
pselect存在的原因是:当等待的要么是信号,要么是文件描述符状态准备好,一个原子操作需要防止竞争(死锁)。
假定出现下面的情况:
信号处理器设置一个全局标志位然后返回。对全局标志检测之后跟着一个select调用,而信息来临刚好在检测之后,select调用之前,那么select会无限期挂起。
与之相对的是,pselect允许先阻塞信号,处理完信号之后,再调用pselect,设置想要的sigmask来避免竞争。
timeout
The timeout
The time structures involved are defined in sys/time.h
and look like
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
and
struct timespec {
long tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
Some code calls select() with all three sets empty, nfds zero, and a non-NULL timeout as a fairly portable way to sleep with subsecond precision.
一些代码调用select时把除了timeout的其它参数都留空,来作为一种实现休眠亚秒精确度时间的相当轻便的方式。
On Linux, select() modifies timeout to reflect the amount of time not slept; most other implementations do not do this. (POSIX.1-2001 permits either behavior.) This causes problems both when Linux
code which reads timeout is ported to other operating systems, and when code is ported to Linux that reuses a struct timeval for multiple select()s in a loop without reinitializing it. Consider
timeout to be undefined after select() returns.
在Linux,select() 修改 timeout 来反映出没有休眠的时间;很多其他的程序并不这样做(POSIX.1-2001版本允许这2种行为).当Linux的代码到了别的操作系统中,和在一个循环中的多个select()的struct timeval 重用时都未初始化的代码用在了Linux中,就会产生问题.所以就将timeout 在select()返回后就当作是未定义的就好.
返回值
On success, select() and pselect() return the number of file descriptors contained in the three returned descriptor sets (that is, the total number of bits that are set in readfds, writefds,
exceptfds) which may be zero if the timeout expires before anything interesting happens. On error, -1 is returned, and errno is set appropriately; the sets and timeout become undefined, so do not
rely on their contents after an error.
成功的话,select() 和 pselect() 都返回在3个描述数组中的fd数目,
一旦timeout超时而我们感兴趣的事情还未发生,将返回0
错误的话,返回-1并自动设置errno .文件描述符集和timeout会变为未定义的,所以当错误发生后不要依赖它们中的内容。
错误码
EBADF An invalid file descriptor was given in one of the sets. (Perhaps a file descriptor that was already closed, or one on which an error has occurred.)
EINTR A signal was caught; see signal(7).
EINVAL nfds is negative or the value contained within timeout is invalid.
ENOMEM unable to allocate memory for internal tables.
EBADF : 一个无效的fd给了标志位.(可能一个fd已经关闭了,或者已经发生了错误)
EINTR : 信号产生了中断
EINVAL : nfds 是负数或者在timeout 中包含的值是无效的
ENOMEM : 不能为内部的列表分配内存