IO 模式
IO 模式
对于 Linux 的 network IO:
一次 IO 访问(以read举例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区 copy 到应用程序的地址空间。所以说,当一个 read 操作发生时,它会经历两个阶段:
- 等待数据准备 (Waiting for the data to be ready)
- 将数据从内核 copy 到进程中 (Copying the data from the kernel to the process)
对应的就有5种 IO 模式
- 阻塞 I/O(blocking IO)
- 非阻塞 I/O(nonblocking IO)
- I/O 多路复用(IO multiplexing)
- 信号驱动 I/O(signal driven IO)
- 异步 I/O(asynchronous IO)
分类:
IO 操作时阻塞:同步 IO(包含阻塞 IO、非阻塞 IO、IO 多路复用)都需要程序自己主动获取数据
IO 操作时不阻塞:异步 IO
blocking IO
读取时如果没有数据就会发生阻塞,从内核空间 copy 到用户空间也会阻塞
nonblocking IO
读取时如果没有数据也不会发生阻塞,从内核空间 copy 到用户空间会阻塞
实现原理:不断地调用 recvfrom 检查是否有数据,如果没有数据内核会直接返回错误,直到有数据
IO multiplexing
和 blocking IO 相似,但是一个进程可以监视多个描述符,只要其中其中一个就绪就会返回,从内核空间 copy 到用户空间也会阻塞
实现原理:一次发送多个描述符,等待内核返回就绪确认之后才调用 recvfrom 接收数据
种类(事件驱动)
select
select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。select的一 个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但 是这样也会造成效率的降低。
poll
poll 相比 select 没有最大的数量限制
epoll
相对于 select 和 poll 来说,epoll 更加灵活,没有描述符限制
epoll 不需要每次系统调用时向内核传递所有文件描述符,而是使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,当增加新的描述符时只需要将其添加到时间表中
对于内核,也不要每次都遍历所有文件描述符
这样在用户空间和内核空间的 copy 只需一次
gevent 和 epoll 都通过 libevent.so 实现,但协程关注更多的是任务的切换
selector
selectors 封装了 select 和 epoll,默认使用 epoll,如果无法使用 epoll 就换成 select <-点击查看
asynchronous IO
从内核空间 copy 到用户空间都不阻塞
实现原理:内核收到请求直接返回,等待数据 copy 到用户内存后,发送信号告诉用户进程