select 模型
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
nfds: 用户需要关注的最大文件描述符
readfds、writefds、exceptfds:用户关注的句柄的bitmap。
timeout:超时时间
fd_set的定义如下:
typedef __kernel_fd_set fd_set;
#define __NFDBITS (8 * sizeof(unsigned long))
#define __FD_SETSIZE 1024
#define __FDSET_LONGS (__FD_SETSIZE/__NFDBITS)
typedef struct {
unsigned long fds_bits [__FDSET_LONGS];
} __kernel_fd_set;
由上述代码可见select能监听的最大句柄数量由 __FD_SETSIZE =1024所限定了,因为FDSET_LONGS为 __FD_SET_SIZE/__NFDBITS,NFDBITS定义为8*4=32,表示有32位,即一个unsigned long在bitmap中就可以表示32个数,总共需要的数组长度就为1024/32个,这也是使用bitmap的优点。
select的缺点:
(1)默认最大监听句柄数为1024
(2)需要从用户态拷贝到内核
(3)需要遍历所有句柄,不管该句柄是否有无数据可读,造成浪费。
epoll模型
epoll使用了红黑树来保存监听的句柄,故没有最大句柄数限制的说法,而且采用了事件触发机制,不用遍历所有句柄,只需要注册事件,事件发生时就能返回相应句柄进行处理,O(n)的时间复杂度降到了O(1)。
epoll的LT和ET模式:
LT是每次epoll_wait的时候,只要有数据可读或可写就能返回。写程序比较简单,只需要每次返回后进行对应处理就行,不用管是否缓冲区已经读取完毕或者写完毕就可以再次调用epoll_wait。
ET是每次epoll_wait的时候,所有数据接收完毕后才返回,而调用recv读取后一定要再次recv,如果返回EAGAIN错误后才再次调用epoll_wait。
ET 使用准则,只有出现EAGAIN错误才调用epoll_wait。