在一次使用select()的过程中,发现select的value of return及循环调用select()其中timeout的值似乎有些超出预期,于是查了一下,发现有些地方真的没有注意到。
Linux man manual:
/* According to POSIX.1-2001 */
#include <sys/select.h>
/* According to earlier standards */
#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 *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);
#include <sys/select.h>
int pselect(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, const struct timespec *timeout,
const sigset_t *sigmask);
Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
pselect(): _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600
对于select()的返回值判断,还是依据FD_ISSET()的返回值来判断是否可以接收。
APUE 2 描述的select的返回值
关于select的返回值有三种可能:
1、返回值-1,表示出错。
2、返回值0,表示描述符没有准备好。若指定的描述符没有准备好,且指定的时间超时,返回0.同时,描述符集被清零。
3、正返回值,表示已经准备好的描述符数,该值是三个描述符集中已经准备好的描述符数之和,如果统一描述符的写和读都已准备好,则返回值中将为2,。
Linux man manual:
uccess, select() and pselect() return the number of file descrip-
tors 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()如果超时,是可能返回zero,而不是标准规定,如此如果超时根据等于零判断,有可能会出现问题。
就是超时了,但是仍然返回正确。
关于 struct timeval *timeout
APUE2描述的返回值:
timeout->tv_sec !=0 || timeout->tv_usec != 0
等待指定的秒数和微秒数。当指定的描述符之一已经准备好,或者指定的时间值已经超过时,立即返回。如果超时时,还没有一个描述符准备好,则返回值是0.同时,这种等待可能被捕捉到的信号打断。
这里如果有准备好的描述符时,返回值是多少并没有提及。
在linux的man manual中
On Linux, select() modifies timeout to reflect the amount of time not
slept; most other implementations do not do this. (POSIX.1-2001 per-
mits 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() modifies timeout,这样就造成了timeout可能是未知的。所以最后一句指明,在调用select()后,timeout的知就是未定义的。
在自己使用的ARM-Linux中,
timeout的返回值如下:
struct timeval的输入参数,被select修改了。
超时时的的time返回值:
sTv.tv_sec1: 20
sTv.tv_sec2: 0
未超时时的返回值:
sTv.tv_sec1: 20
sTv.tv_sec2: 18
由此观察可知,
超时时返回零,未超时时返回,剩余的时间。
所以,对于使用select时timeout的后续使用要小心一些。
同时pselect()对timeout作了const限定,它的值是不会被修改,如果循环使用还是pselect 更合适。