选择
选择是进行 I / O 多路复用的另一种方法。其中一个优点是 winsock API 存在。而且,在 Linux 上,select()
修改超时以反映未睡眠的时间量; 大多数其他实现不会这样做。 (POSIX.1 允许任何行为。)
poll 和 select 都有 ppoll 和 pselect 替代方案,允许在等待事件期间处理输入信号。并且它们都变得很慢,有大量的文件描述符(一百个以上),所以选择特定于平台的调用是明智的,例如 Linux 上的 epoll
和 FreeBSD 上的 kqueue
。或者切换到异步 API(例如 POSIX aio
或类似 IO 完成端口的东西)。
选择呼叫具有以下原型:
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
fd_set
是文件描述符的位掩码数组,
nfds
是 set + 1 中所有文件描述符的最大数量。
选择的代码片段:
fd_set active_fd_set, read_fd_set;
FD_ZERO (&active_fd_set); // set fd_set to zeros
FD_SET (sock, &active_fd_set); // add sock to the set
// # define FD_SETSIZE sock + 1
while (1) {
/* Block until input arrives on one or more active sockets. */
read_fd_set = active_fd_set; // read_fd_set gets overriden each time
if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0) {
// handle error
}
// Service all file descriptors with input pending.
for (i = 0; i < FD_SETSIZE; ++i) {
if (FD_ISSET (i, &read_fd_set)) {
// there is data for i
}
}
请注意,在大多数 POSIX 实现中,与磁盘上的文件关联的文件描述符都是阻塞的。所以写入文件,即使这个文件是在 writefds
中设置的,也会阻塞,直到所有字节都不会被转储到磁盘上