来源文章:
最多能创建多少个TCP连接?
你管这破玩意叫 IO 多路复用?
创建一个socket都需要哪些系统资源?
- 内存资源
- CPU资源
- 端口号资源
- 文件描述符资源
- 线程资源
IO多路复用的发展历程
阻塞IO
read数据过程完全阻塞(不论有没有数据)
可把read放在子线程中,实现主线程非阻塞
非阻塞 IO
在无数据时,read不再等待数据,而是返回无数据的状态值
在读取数据时read仍然是阻塞的。
IO 多路复用
因为每一次read都是一次系统调用,所以如果一次read能够批量取回多个连接的数据状态,这将大大提高性能
select系统调用
参数为一个文件描述符列表,在内核中遍历并标记文件描述符,返回标记过状态的文件描述符列表,用户进程可以根据此状态知道哪些描述符的数据已经准备好了。
问题:
- select 调用需要传入 fd 数组,需要拷贝一份到内核,高并发场景下这样的拷贝消耗的资源是惊人的。(可优化为不复制)
- select 在内核层仍然是通过遍历的方式检查文件描述符的就绪状态,是个同步过程,只不过无系统调用切换上下文的开销。(内核层可优化为异步事件通知)
- select 仅仅返回可读文件描述符的个数,具体哪个可读还是要用户自己遍历。(可优化为只返回给用户就绪的文件描述符,无需用户做无效的遍历)
poll系统调用
它和 select 的主要区别就是,去掉了 select 只能监听 1024 个文件描述符的限制。
epoll系统调用
针对select的缺点做了如下改进:
- 内核中保存一份文件描述符集合(红黑树),无需用户每次都重新传入,只需告诉内核修改的部分即可。
- 内核不再通过轮询的方式找到就绪的文件描述符,而是通过异步 IO 事件唤醒。
- 内核仅会将有 IO 事件的文件描述符返回给用户,用户也无需遍历整个文件描述符集合。
并发设计模式
reactor