I/O 多路转接,为了使用这种技术,先构造一张我们感兴趣的描述符的列表,然后调用一个函数,直到这些描述符中的一个已准备好进行 I/O 时,该函数才返回。poll、pselect 和 select 这3个函数使得我们能够指向 I/O 多路转接。
传给 select 的参数告诉内核:
从 select 返回时,内核告诉我们:
使用这种返回信息,就可调用相应的 I/O 函数(一般是 read 或 write),并且确知该函数不会阻塞。
#include
int select(int maxfdp1, fd_set *restrict readfds, fd_set *restrict wirtefds, fd_set *restrict exceptfds, struct timeval *restrict tvptr);
//返回值:准备就绪的描述符数目;若超时,返回0,若出错,返回-1
先说明最后一个参数,它指定愿意等待的时间长度,单位是秒和微妙。有以下 3 种情况:
中间 3 个参数 readfds、writefds 和 exceptfds 是指向描述符集的指针。这 3 个描述符集说明了我们关心的可读、可写或处于异常条件的描述符集合。每个描述符集存储在一个 fd_set 数据类型中。
对于 fd_set 数据类型,唯一可以进行的处理是:分配一个这种类型的变量,将这种类型的一个变量值赋给同类型的另一个变量,或对这种类型的变量使用下列 4 个函数的一个:
#include
int FD_ISSET(int fd, fd_set *fdset);
//返回值:若 fd 在描述符集中,返回非 0 值;否则,返回0
void FD_CLR(int fd, fd_set *fdset);
void FD_SET(int fd, fd_set *fdset);
void FD_ZERO(fd_set *fdset);
这些接口可实现为宏或函数。调用 FD_ZERO 将一个 fd_set 变量的所有位设置为 0.要开启描述符集中的一位,可以调用 FD_SET。调用 FD_CLR 可以清除一位。最后,调用 FD_ISSET 测试描述符集中的一个指定位是否已打开。
在声明了一个描述符集之后,必须用 FD_ZERO 将这个描述符集置为 0,然后在其中设置我们关心的各个描述符的位。
select 的中间 3 个参数中的任意一个可以是空指针,这表示对相应条件并不关心。
select 第一个参数 maxfdp1 的意思是“最大文件描述符编号加 1”。考虑所有 3 个描述符集,在 3 个描述符集中找出最大描述符编号值,然后加 1,这就是第一个参数值。也可将第一个参数设置为 FD_SETSIZE 这是
select 有 3 个可能的返回值
对于“准备好”的含义要作一些更具体的说明
#include
int pselect(int maxfdp1, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, const struct timespec *restrict tsptr, const sigset_t *restrict sigmask);
//返回值:准备就绪的描述符数目;若超时,返回i0;若出错,返回 -1
除了以下几点外,pselect 与 select 相同:
poll 函数类似于 select,但是程序员接口有所不同。poll 函数可用于任何类型的文件描述符。
#include
int poll(struct pollfd fdarray[], nfds_t nfds, int timeout);
//返回值:准备就绪的描述符数目;若超时,返回0;若出错,返回-1
struct poofd{
int fd;
short events;
short revents;
};
与 select 不同,poll 不是为每个条件(可读性、可写性和异常条件)构造一个描述符集,二十构造一个 pollfd 结构的数组,每个数组元素指定一个描述符编号以及我们对该描述符感兴趣的条件。
fdarray 数组中的元素数由 nfds 指定。
应将每个数组元素的 events 成员设置为下表中所示值得一个或几个,通过这些值告诉内核我们关系的是每个描述符的那些事件。返回时,revents 成员由内核设置,用于说明每个描述符发送了哪些事件。
标志名 | 说明 |
POLLIN | 可以不阻塞地读高优先级数据以外的数据 |
POLLRDNORM | 可以不阻塞地读普通数据 |
POLLRDBAND | 可以不阻塞地读优先级数据 |
POLLPRI | 可以不阻塞地读高优先级数据 |
POLLOUT | 可以不阻塞地写普通数据 |
POLLWRNORM | 与 POLLOUT 相同 |
POLLWRBAND | 可以不阻塞地写优先级数据 |
POLLERR | 已出错 |
POLLHUP | 已挂断 |
POLLNVAL | 描述符没有引用一个打开文件 |
上表中的前 4 行测试的是可读性,接下来的 3 行测试的是可写性,最后 3 行测试的是异常条件。最后 3 行是由内核在返回时设置的。即使在 events 字段中没有指定这 3 个值,如果相应条件发生,在 revents 中也会返回它们。
当一个描述符被挂断(POLLHUP)后,就不能再写该描述符,但是有可能仍然可以从该描述符读取到数据。
poll 的最后一个参数指定的是我们愿意等多长时间。有 3 种不同的情形: