if(–nready == 0) break;
}
}
}
正如刚刚的动图中所描述的,其直观效果如下 。(同一个动图消耗了你两次流量,气不气?)
可以看出几个细节:
1. select 调用需要传入 fd 数组,需要拷贝一份到内核,高并发场景下这样的拷贝消耗的资源是惊人的 。(可优化为不复制)
2. select 在内核层仍然是通过遍历的方式检查文件描述符的就绪状态,是个同步过程,只不过无系统调用切换上下文的开销 。(内核层可优化为异步事件通知)
3. select 仅仅返回可读文件描述符的个数,具体哪个可读还是要用户自己遍历 。(可优化为只返回给用户就绪的文件描述符,无需用户做无效的遍历)
整个 select 的流程图如下 。
可以看到,这种方式,既做到了一个线程处理多个客户端连接(文件描述符),又减少了系统调用的开销(多个文件描述符只有一次 select 的系统调用 + n 次就绪状态的文件描述符的 read 系统调用) 。
poll
poll 也是操作系统提供的系统调用函数 。
int poll(struct pollfd *fds, nfds_tnfds, int timeout);
struct pollfd {
intfd; /*文件描述符*/
shortevents; /*监控的事件*/
shortrevents; /*监控事件中满足条件返回的事件*/
};
它和 select 的主要区别就是,去掉了 select 只能监听 1024 个文件描述符的限制 。
epoll
epoll 是最终的大 boss,它解决了 select 和 poll 的一些问题 。
还记得上面说的 select 的三个细节么?
1. select 调用需要传入 fd 数组,需要拷贝一份到内核,高并发场景下这样的拷贝消耗的资源是惊人的 。(可优化为不复制)
2. select 在内核层仍然是通过遍历的方式检查文件描述符的就绪状态,是个同步过程,只不过无系统调用切换上下文的开销 。(内核层可优化为异步事件通知)
3. select 仅仅返回可读文件描述符的个数,具体哪个可读还是要用户自己遍历 。(可优化为只返回给用户就绪的文件描述符,无需用户做无效的遍历)
所以 epoll 主要就是针对这三点进行了改进 。
1. 内核中保存一份文件描述符集合,无需用户每次都重新传入,只需告诉内核修改的部分即可 。
2. 内核不再通过轮询的方式找到就绪的文件描述符,而是通过异步 IO 事件唤醒 。
【无法连接到internet怎么解决 无法连接到internet】3. 内核仅会将有 IO 事件的文件描述符返回给用户,用户也无需遍历整个文件描述符集合 。
具体,操作系统提供了这三个函数 。
第一步,创建一个 epoll 句柄
int epoll_create(int size);
第二步,向内核添加、修改或删除要监控的文件描述符 。
int epoll_ctl(
int epfd, int op, int fd, struct epoll_event *event);
第三步,类似发起了 select() 调用
int epoll_wait(
int epfd, struct epoll_event *events, int max events, int timeout);
使用起来,其内部原理就像如下一般丝滑 。
如果你想继续深入了解 epoll 的底层原理,推荐阅读飞哥的《图解 | 深入揭秘 epoll 是如何实现 IO 多路复用的!》,从 linux 源码级别,一行一行非常硬核地解读 epoll 的实现原理,且配有大量方便理解的图片,非常适合源码控的小伙伴阅读 。
后记
大白话总结一下 。
一切的开始,都起源于这个 read 函数是操作系统提供的,而且是阻塞的,我们叫它 阻塞 IO 。
推荐阅读
- 全球连锁超市排名 连锁超市排名
- apple id 连接服务器时出现问题
- 无证驾驶会牵连车主吗
- 什么叫炼乳裙
- 情侣表白经典句子
- ipad屏幕失灵无法关机 ipad屏幕失灵
- 七代雅阁车门无法遥控锁
- 电信itv关闭付费功能
- 韩信怎么连招
- 我的世界与服务器断开连接