epoll相比select,poll的2个改进点

改进点

  1. epoll把select,poll的功能进行了拆分: 1个api拆分为3个api
  2. 维护就绪列表,不用每次遍历全部fd寻找就绪fd进行处理,而是直接拿到就绪fd进行处理。=> 空间换时间,我把就绪fd存放到某个地方,就不需要遍历了。

// 附录2

int s = socket(AF_INET, SOCK_STREAM, 0);  
bind(s, ...)
listen(s, ...)
int fds[] =  存放需要监听的socket
while(1){
    int n = select(..., fds, ...) // ydd:可以看到这里,每次都需要把全部fds集合传递进去。=> 能不能只传1次
    for(int i=0; i < fds.count; i++){ // ydd: 每次都需要遍历全部fd,然后查找可读的fd进行操作。=> 能不能只返回有事件触发的fd
        if(FD_ISSET(fds[i], ...)){
            //fds[i]的数据处理
        }
    }
}
  1. select低效的原因之一是将“维护等待队列”和“阻塞进程”两个步骤合二为一。 // 因为select只有1个api, 整个都放在while循环中,存在冗余和浪费。
    epoll将这两个操作分开,先用epoll_ctl维护等待队列,再调用epoll_wait阻塞进程。// epoll有3个api,把事情分开做了,只有最后一个api放在while循环中。

  2. select低效的另一个原因在于程序不知道哪些socket收到数据,只能一个个遍历。如果内核维护一个“就绪列表”,引用收到数据的socket,就能避免遍历。如下图所示,计算机共有三个socket,收到数据的sock2和sock3被rdlist(就绪列表)所引用。当进程被唤醒后,只要获取rdlist的内容,就能够知道哪些socket收到数据。

int s = socket(AF_INET, SOCK_STREAM, 0);   
bind(s, ...)
listen(s, ...)

int epfd = epoll_create(...);
epoll_ctl(epfd, ...); //将所有需要监听的socket添加到epfd中

while(1){
    int n = epoll_wait(...)
    for(接收到数据的socket){
        //处理
    }
}

epoll引入了只触发一次的事件通知方式

条件触发

// 附录1
满足条件一直触发,这也是select,poll的触发方式。

  • 附录1的前几个评论
https://github.com/linuxxiaoyu/block
执行后都会重复打印”need read socket_fd“和“poll need to read”,所以select和poll应该都是条件触发

边缘触发

只触发一次,所以效率很高,特有的触发方式。
Q: 如果只触发一次,那数据没有读取完,怎么办?

参考

  1. 23 | Linux利器:epoll的前世今生
  2. 如果这篇文章说不清epoll的本质,那就过来掐死我吧! (1)
原文地址:https://www.cnblogs.com/yudidi/p/12662392.html