select
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
select函数监视的文件描述符分3类, 分别是writefds、readfds、和exceptfds。调用后select函数会阻塞, 直到有描述符就绪(有数据 可读、可写、或者有except),或者超时(timeout指定等待时间), 函数返回。- 当
select函数返回后,通过遍历fdset,来找到就绪的文件描述符 select目前几乎在所有平台上都支持, 其良好跨平台支持是它的一个优点select的缺点在于单个进程能够监视的文件描述符的数量有最大限制, 在Linux上一般为1024
poll
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
不同于select使用三个位图来表示三个fdset的方式, poll使用一个pollfd的指针实现
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
pollfd结构包含了要监视的event和发生的eventpollfd并没有最大数量限制(但是数量过大后性能也是会下降)- 和
select函数一样,poll返回后,需要轮询pollfd来获取就绪的文件描述符
select和poll都需要在返回后,通过循环遍历文件描述符来获取已经就绪的socket。事实上,同时连接的大量客户端在某一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降
epoll
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
- 在
select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描 epoll事先通过epoll_ctl()来注册一 个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知- 去掉了遍历文件描述符,而是通过监听回调的的机制
epoll的优点- 监视的描述符数量不受限制,它所支持的fd上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,一般来说这个数目和系统内存关系很大
- IO的效率不会随着监视fd的数量的增长而下降。
epoll不同于select和poll轮询的方式,而是通过每个fd定义的回调函数来实现的。只有就绪的fd才会执行回调函数