网络轮训器
[TOC]
问题及思考
- 异步IO 与 非阻塞IO,这两个概念有什么不一样?
IO 模型
操作系统的IO模型主要有:
- Blocking IO - 阻塞塞 I/O
- Nonblocking IO - 非阻塞 I/O
- IO multiplexing - I/O 多路复用
- Signal-driven IO - 信号驱动式I/O(异步阻塞)
- Asynchronous IO - 异步IO
同步:调用端会一直等待服务端响应,直到返回结果
异步:调用端发起调用之后不会立刻返回,不会等待服务端响应
阻塞:服务端返回结果之前,客户端线程会被挂起,此时线程不可被 CPU 调度,线程暂停运行
非阻塞:在服务端返回前,函数不会阻塞调用端线程,而会立刻返回
阻塞与非阻塞 I/O
阻塞非阻塞是描述调用者
的,根据调用者是否阻塞自身运行,可以把 I/O 分为阻塞 I/O 和非阻塞 I/O。
同步 I/O 和异步 I/O
根据 I/O 响应的通知方式的不同,可以把 I/O 分为同步 I/O 和异步 I/O。同步IO和异步IO是描述被调用者的状态。
- 同步 I/O,是指收到 I/O 请求后,系统不会立刻响应应用程序;等到处理完成,系统才会响应应用程序 I/O 结果。
- 异步 I/O,是指收到 I/O 请求后,系统会立刻响应, I/O 请求已经收到,随后再去异步处理;等处理完成后,系统再通过事件通知的方式,告诉调用者结果。
为什么这么分类
阻塞/非阻塞和同步/异步,其实就是两个不同角度的 I/O 划分方式。它们描述的对象也不同,
- 阻塞/非阻塞针对的是 I/O 调用者,
- 同步/异步针对的是 I/O 执行者。
比如在 Linux I/O 调用中,
-
在进行文件读写是,系统调用 read 和 write 就是典型的阻塞 IO, 在响应之前调用线程会被阻塞。
-
而 aio_read 是异步读,系统收到 AIO 读请求后不等处理就返回了,而具体的 read 结果,再通过回调异步通知应用程序。
在网络套接字的接口中,
- 使用 send() 直接向套接字发送数据时,如果套接字没有设置 O_NONBLOCK 标识,那么 send() 操作就会一直阻塞,当前线程也没法去做其他事情。这就是阻塞 IO。
- 如果是 epoll,系统会告诉你这个套接字的状态,那就可以用非阻塞的方式使用。当这个套接字不可写的时候,你可以去做其他事情,比如读写其他套接字。
I/O 多路复
I/O 多路复用被用来处理同一个事件循环中的多个 I/O 事件。I/O 多路复用需要使用特定的系统调用支持,不同的操作系统也都实现了自己的 I/O 多路复用函数,例如:select
, poll
, epoll
、kqueue
和 evport
等。
select
- 在一定时间内轮询一定数量的文件描述,找出就绪连接。可同时监听 1024 个文件描述符,可以在一个进程内维持1024个连接。
- 需要不断循环找出就绪者,时间复杂度O(n);
- 缺点很明显,需要不断轮询,而且文件描述符的限制在了 1024 之内。
poll
- 与select类似,也是在一定时间内轮训一定数量的文件描述,找出就绪者,但是使用链表存储文件描述符,摆脱了 1024 的数量上限。,
- 需要遍历所有注册文件描述来找出就绪者,最大文件描述 65535,找出就绪者时间复杂度O(n)
select/poll缺点
- 需要循环检测连接是否有事件,
- 如果服务器有1万个连接,在某一时间只有一个连接向服务器发送了数据,select/poll 需要做循环1万次,其中只有1次是命中的,剩下的9999次都是无效的,白白浪费了CPU资源。
epoll
- 内核事件表
- 实现和使用与select和poll有较大差异,
- Linux 2.6内核提供了新的epoll系统调用,可以维持无限数量的连接,而且无需轮询,真正解决了C10K问题。
- 现在各种高并发异步IO的服务器程序都是基于epoll实现的,比如Nginx、Node.js、Erlang、Golang。
- Node.js这样单进程单线程的程序,都可以维持超过1百万TCP连接,要归功于epoll技术。
- 由一组函数组成,而不是单个函数、
- 最大文件描述65535
- 把文件描述的事件放在内核的事件表中,而无需像select和poll每次调用都要从用户空间传入事件集,
- 通过epoll_wait 检测就绪事件,避免了像前两者那样去轮训,极大提高了索引就绪文件描述的效率
- 找出就绪者时间复杂度O(1)
- 通过内核检测就绪者,触发事件回调,并放入就绪事件队列,内核把就绪事件拷贝到用户空间,执行处理逻辑
- 适合很多连接,但是只有少部分活跃连接的,若活跃连接很多,也未必比poll高效
Go IO
Go 语言在网络轮询器中使用 I/O 多路复用模型处理 I/O 操作。Go 也根据不同的操作系统实现了不同的多路复用实现。
src/runtime/netpoll_epoll.go
src/runtime/netpoll_kqueue.go
src/runtime/netpoll_solaris.go
src/runtime/netpoll_windows.go
src/runtime/netpoll_aix.go
src/runtime/netpoll_fake.go
TODO