该函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或者多个事件发生或者经历一段时间后唤醒他。
#include<sys/select.h>
#include<sys/time.h>
int select(int maxfdpl, fd_set* readset, fd_set* writeset, fd_set* exceptset, const struct timeval* timeout);
返回:若有就绪描述符则为其数目,若超时则为0,若出错则为-1
参数timeout,他告知内核等待所指定描述符中的任何一个就绪可花多长时间。其timeval结构用于指定这段时间的秒数和微秒数。
struct timeval {
long tv_sec;
long tv_usec;
};
这个参数有以下三种可能。
1) 永远等待下去,仅有一个描述符准备好I/O时才返回。为此,我们把该参数设置为NULL
2) 等待一段固定时间,在有一个描述符准备好I/O时返回,但是不超过由该参数指向的timeval结构中指定的秒数和微妙数。
3) 根本不等待,检查描述符后立刻返回,这称为轮询(polling)。为此,该参数必须指向的timeval结构中的秒数和微秒数必须为0.
前两种情形的等待通常会被进程在等待期间捕获的信号中断,并从信号处理函数返回。尽管timeval结构允许我们指定一个微妙级的分辨率,然而内核支持的真实分辨率往往粗糙的多,可能是毫秒级的。
中间的三个参数readset、writeset、和exceptset指定我们要让内核测试读、写和异常条件的描述符。目前支持的异常条件只有两个:
1) 某个套件字的带外数据的到达
2) 某个已置为分组模式的伪终端存在可从其主端读取的控制状态信息(?)
如果我们对某一个的条件不感兴趣,就可以把他置为NULL。
fd_set描述符集设置的4个宏
FD_ZERO(fd_set* fdset);
FD_SET(int fd, fd_set* fdset);
FD_CLR(int fd, fd_set* fdset);
FD_ISSET(int fd, fd_set* fdset);
select函数修改由指针readset、writeset、exceptset所指向的描述集,这三个参数是值-结果参数。调用该函数时,我们指定我们所关心的描述符值,该函数返回时,结果将指示那些描述符就绪。我们用FD_ISSET来测试
maxfdpl参数指定测试的描述符个数,他的值为待测试的最大描述符加1。
描述符就绪条件
1) 可读条件
a)该套接字接收缓冲区中的数据字节数大于等于套接字接收缓冲区低水位的当前大小。
b)该连接的读半部关闭,也就是收到了FIN的TCP连接。对这样的套接字的读操作将不阻塞并返回0
c)该套接字是一个监听套接字且已完成的连接数不为0。对这样的套接字的accept通常不会阻塞。
d)其上有一个套接字错误待处理,对这样的套接字的读操作将不阻塞并返回-1,也就是返回一个错误,同时把errno设置成确切的错误条件。这些待处理错误也可以通过指定SO_ERROR套接字选项调用getsockopt获取并清除。
2)可写条件
a)该套接字发送缓冲区中的可用空间字节数大于等于套接字发送缓冲区低水位标记的当前大小
b)该连接的写半部关闭。对这样的套接字的写操作将产生SIGPIPE信号
c)使用非阻塞式connect的套接字已经建立连接,或者connect已经以失败告终。
d)其上有一个套接字错误待处理,对这样的套接字的写操作将不阻塞并返回-1,也就是返回一个错误,同时把errno设置成确切的错误条件。这些待处理错误也可以通过指定SO_ERROR套接字选项调用getsockopt获取并清除。
3)异常条件
如果一个套接字存在带外数据或者仍处于带外标记,那么他有异常条件待处理。
汇总如下图所示