对比I/O多路复用的select, poll, epoll函数

946 阅读2分钟

select

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
  • select 函数监视的文件描述符分3类, 分别是writefdsreadfds、和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和发生的event
  • pollfd并没有最大数量限制(但是数量过大后性能也是会下降)
  • select函数一样,poll返回后,需要轮询pollfd来获取就绪的文件描述符

selectpoll都需要在返回后,通过循环遍历文件描述符来获取已经就绪的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的优点
    1. 监视的描述符数量不受限制,它所支持的fd上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,一般来说这个数目和系统内存关系很大
    2. IO的效率不会随着监视fd的数量的增长而下降。epoll不同于selectpoll轮询的方式,而是通过每个fd定义的回调函数来实现的。只有就绪的fd才会执行回调函数

参考

Linux IO模式及select、poll、epoll详解