改进点
- epoll把select,poll的功能进行了拆分: 1个api拆分为3个api
- 维护就绪列表,不用每次遍历全部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]的数据处理
}
}
}
-
select低效的原因之一是将“维护等待队列”和“阻塞进程”两个步骤合二为一。 // 因为select只有1个api, 整个都放在while循环中,存在冗余和浪费。
epoll将这两个操作分开,先用epoll_ctl维护等待队列,再调用epoll_wait阻塞进程。// epoll有3个api,把事情分开做了,只有最后一个api放在while循环中。 -
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: 如果只触发一次,那数据没有读取完,怎么办?