一、select用途
select是IO多路复用模型中的一种,属于标准的IO多路复用,在Windows、Linux下都可以使用。IO多路复用同时监听多个IO,一次系统调用判定所有IO的可读写状态,会一直阻塞或者指定一个超时时间,直到一个或多个文件描述符集合称为就绪态。select避免了多次的系统调用和阻塞,但不适合文件数量庞大、IO流量频繁的时候,会随着fd的数量增大性能下降明显。
二、接口定义
windows定义
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
const struct timeval *timeout)
linux定义
int select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout)
其他fd_set操作接口
FD_CLR(inr fd,fd_set* set);将描述符fd从fdset所指向的集合中移除
FD_SET(int fd,fd_set*set);将描述符fd添加到fdset所指向的集合中
FD_ZERO(fd_set *set); 将fdset指向的集合初始化为空
FD_ISSET(int fd,fd_set *set);测试描述词组set中相关fd 的位是否为真
select参数详解:
nfds:select中监视的文件句柄数,一般设为要监视的文件中的最大文件号加一。
readfds:检测读是否就绪的文件描述符集合
writefds :检测写是否就绪的文件描述符集合
exceptfds:检测异常情况是否发生的文件描述符集合
(1、信包模式下伪终端主设备上从设备状态发生改变;2、流式套接字接收到了带外数据)
timeout: 设为NULL,等待直到某个文件描述符发生变化;设为0秒0毫秒,不等待直接返回;
设为大于0的值,有描述符变化或超时时间到才返回。
select返回值:负数表示有错误发生;大于等于0,表示有n个描述符就绪。具体是哪个,
还需要用FD_ISSET遍历判定。
三、windows的select源码
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, const struct timeval *timeout)
#ifndef FD_SETSIZE
#define FD_SETSIZE 64
#endif /* FD_SETSIZE */
typedef struct fd_set {
unsigned int fd_count; /* how many are SET? */
unsigned int fd_array[FD_SETSIZE]; /* an array of SOCKETs */
} fd_set;
#define FD_ZERO(set) (((fd_set *)(set))->fd_count=0)
#define FD_SET(fd, set) do { \
u_int __i; \
for (__i = 0; __i < ((fd_set *)(set))->fd_count; __i++) { \
if (((fd_set *)(set))->fd_array[__i] == (fd)) { \
break; \
} \
} \
if (__i == ((fd_set *)(set))->fd_count) { \
if (((fd_set *)(set))->fd_count < FD_SETSIZE) { \
((fd_set *)(set))->fd_array[__i] = (fd); \
((fd_set *)(set))->fd_count++; \
} \
} \
} while(0, 0)
#define FD_ISSET(fd, set) __WSAFDIsSet((SOCKET)(fd), (fd_set *)(set))
四、linux的select源码
int select (int __nfds, fd_set *__readfds, fd_set *__writefds, fd_set *__exceptfds, struct timeval *__timeout);
#define __FD_SET(d, set) ((void) (__FDS_BITS (set)[__FD_ELT (d)] |= __FD_MASK (d)))
#define __FD_CLR(d, set) ((void) (__FDS_BITS (set)[__FD_ELT (d)] &= ~__FD_MASK (d)))
#define __FD_ISSET(d, set) ((__FDS_BITS (set)[__FD_ELT (d)] & __FD_MASK (d)) != 0)
# define __FD_ZERO(set) \
do { \
unsigned int __i; \
fd_set *__arr = (set); \
for (__i = 0; __i < sizeof (fd_set) / sizeof (__fd_mask); ++__i) \
__FDS_BITS (__arr)[__i] = 0; \
} while (0)
#define __FD_SETSIZE 1024
#define __NFDBITS (8 * (int) sizeof (__fd_mask))
# define __FDS_BITS(set) ((set)->fds_bits)
#define __FD_ELT(d) ((d) / __NFDBITS)
五、select使用代码样例
#pragma once
#include "stdafx.h"
#include <functional>
using namespace std;
#define KIT_FILE_EVENT_READABLE 0x01
#define KIT_FILE_EVENT_WRITABLE 0x02
class KitEventOpManager4Select
{
private:
int _maxFd;
fd_set _readFds;
fd_set _writeFds;
fd_set _cloneReadFds;
fd_set _cloneWriteFds;
function<void(int, int)> _eventHandler;
private:
void resetMaxFd(int changedFd)
{
if (changedFd != _maxFd)
{
return;
}
while (_maxFd >= 0)
{
if(FD_ISSET(_maxFd, &_readFds) > 0 || FD_ISSET(_maxFd, &_readFds) > 0)
{
break;
}
_maxFd--;
}
}
public:
KitEventOpManager4Select(function<void(int,int)> eventHandler): _maxFd(0)
{
FD_ZERO(&_readFds);
FD_ZERO(&_writeFds);
FD_ZERO(&_cloneReadFds);
FD_ZERO(&_cloneWriteFds);
this->_eventHandler = eventHandler;
}
void setReadFd(int fd)
{
FD_SET(fd, &_readFds);
if (fd > _maxFd)
{
_maxFd = fd;
}
}
void setWriteFd(int fd)
{
FD_SET(fd, &_writeFds);
if (fd > _maxFd)
{
_maxFd = fd;
}
}
void clearReadFd(int fd)
{
FD_CLR(fd, &_readFds);
resetMaxFd(fd);
}
void clearWriteFd(int fd)
{
FD_CLR(fd, &_writeFds);
resetMaxFd(fd);
}
void poll(const timeval *time)
{
memcpy(&_cloneReadFds, &_readFds, sizeof(fd_set));
memcpy(&_cloneWriteFds, &_writeFds, sizeof(fd_set)); //fds会被修改,每次select的时候,需要克隆一份
#ifdef __linux__
timeval realTime = *time;
int retval = select(_maxFd + 1, &_cloneReadFds, &_cloneWriteFds, NULL, &realTime);//select
#else
int retval = select(_maxFd + 1, &_cloneReadFds, &_cloneWriteFds, NULL, time);//select
#endif
if (retval > 0)
{
int mask=0;
for (int fd = 0; fd <= _maxFd; fd++)
{
if (FD_ISSET(fd, &_cloneReadFds))
{
mask |= KIT_FILE_EVENT_READABLE;
}
if (FD_ISSET(fd, &_cloneWriteFds))
{
mask |= KIT_FILE_EVENT_WRITABLE;
}
if (mask > 0)
{
_eventHandler(fd,mask);
}
}
}
}
};