首先,这两个个函数都可以用来实现I/O多路转接。
select函数的声明如下:
int select(int maxfdpl, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict tvptr);
select需要注意的事项:
(1)select第一个参数的值为最大描述符+1,因为描述符是从0开始算起的。
(2)select最后一个参数,很特别,如果用好,将会发现select函数有很多用途。最后一个参数是struct timeval *restirct tvptr,其结构体成员如下:
struct timeval
{
long tv_sec;//秒
long tv_usec;//微妙
};
看到这个结构体,我们会想到另外一个表示时间的结构体struct timespc,没错,他们两个很相似,但成员不一样
struct timespec
{
time_t tv_sec;//秒
long tv_nsec;//纳秒
};
由此可见下面的表示时间的结构体精确度更高一些。
继续回到select。。。来说说怎么使用这个时间结构体,分以下几种情况
a. tvptr=NULL;
永远等待,除非捕捉到一个信号则中断返回,select返回值-1,同时errno被置为EINTR。
b. tvptr->tv_sec==0&&tvptr->tv_usec==0;
完全不等待,测试所指定的描述符后就立即返回利用这个可以得到多个描述符的状态而不阻塞select。
c. tvptr->tv_sec!=0||tvptr->tv_usec!=0
等待指定的秒数和微妙数,若指定的描数符之一已准备好,或指定的时间已经超过时立即返回。
如果在超时时,没有一个描述符准备好,则返回0。
(3)从select返回时,用FD_ISSET测试该集中的一个给定位是否仍旧设置。
(4)若所有三个与描述符有关的指针都是空指针,则select提供了比sleep还精确的计时器,因为sleep只能等待数秒,而select最后一个参数可以精确到微妙。
select返回时也有几个地方需要注意下:
(1)返回值-1,表示出错,当所制定的描述符都没有准备好时捕捉到一个中断信号就会出现这种情况,此时,不会修改其中的任何描述符集。
(2)返回值0,表示没有描述符准备好。返回后,所有的描述符都被清为0。
(3)正返回值表示已经准备好的描述符个数。该值是三个描述符集中已准备好的描述符数之和。需要注意的是如果同一描述符已准备好读和写,那么在返回值中该描述符会被重复计数,记为2,此种情况下,三个描述符集中仍旧打开的位对应于已准备好的描述符。
针对select返回的异常状态有两种情况:
1.在网络连接上到达的带外数据
2.在处于数据包模式的伪终端上发生了某些状态
特别主意的是如果在一个描述符上碰到了文件结尾处,则select认为该描述符是可读的,然后调用read,它会返回0值。
select的一个变体pselect也可以实现与select相似的功能,先看pselect的函数声明:
int pselect(int maxfdpl, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restricted exceptfds,const struct timespec *restrict tsptr,const sigset_t *restrict sigmask );
实现与select除了以下几点外,其他相同:
11. select用timeval指定超时值,pselect则用timespec结构。
22. pselect的超时值被声明为const,从而调用pselect时不会改变此值。
33. pselect可使用一可选择的信号屏蔽字,如果sigmask为空,则与select相同,若不为空,则在调用时会以原子方式安装该信号屏蔽字,返回时回复以前的信号屏蔽字。