select和epoll效率差的原因:select是轮询的,而epoll是触发式的,所以epoll的效率高;
select:
原型:
int select(
int nfds, // 本参数忽略,只为与Berkeley套接字兼容
fd_set* readfds, // 可选,指向一组等待可读性检查的套接字的指针
fd_set* writefds, // 可选,指向一组等待可写性检查的套接字的指针
fd_set* exceptfds, // 可选,指向一组等待错误检查的套接字的指针
const struct timeval* timeout // select等待的最长时间,如果想阻塞则设置为NULL
);
- socket数量(文件描述符更准确一些)限制:该模式可操作的socket数量有FD_SETSIZE(宏),内核默认32*32=1024.Windows默认是64,可通过WinSock2.h修改;
- 操作限制:通过遍历FD_SETSIZE个socket来完成调度,不管哪一个socket是活跃的,都要全部遍历一遍:(如果socket数量小并且均很活跃,那么select效率还是不错的)
- 其他:每次调用都要将fd从用户态拷贝到内核态;
- 说明:无论遍历还是拷贝,这种效率都是线性下降的;
- select小结:
select效率底下是select机制本身决定的,与操作系统无关,任何内核在实现select时都必须做遍历——轮询,才能知道哪些socket是准备好的,这会消耗cpu;
当有一个很大的socket集合时,尽管我们只关心那些活跃的socket,即准备好的、我们可以直接拿过来读或者写操作,但是我们不得不每次都将所有的socket填入到FD_SET中,这也会消耗CPU;
poll:
- socket数量几乎没有限制:该模式下的socket对应的fd列表由一个数组保存,大小不限(默认4K),这个值跟系统的内存关系很大;
- 操作限制:同select
- 其他:同样每次调用都需要将包含所有fd的数组从用户态拷贝到内核态;
- 说明:同select
epoll:
- socket数量无限制:同poll;
- 操作无限制:基于内核提供的反射模式(回调机制),有活跃的socket时,内核访问socket的callback(外部注入),不需要遍历轮询,但是当所有的socket都很活跃的时候,这时候所有的callback都被唤醒,会导致资源的竞争,既然要处理几乎所有的socket,那么,遍历是最简单有效的实现方式——select模型;
- epoll小结:
epoll的试用场景就是有大量的socket,但是活跃不是很高的情况;
// 仅仅是一个简单的,概念上的比较,后续会从代码层面深入理解;
参考:
http://www.cnblogs.com/Anker/p/3265058.html
http://blog.chinaunix.net/uid-20665047-id-3595356.html