- IO就是将进程内部数据转移到外部设备或将外部设备的数据迁移到进程内部
- IO过程
- 应用程序进程向OS发起IO调用请求
- OS准备数据,将IO外部设备数据加载到内核缓冲区
- OS拷贝数据,将内核缓冲区数据拷贝到用户进程缓冲区
阻塞IO
-
应用程序进程发起IO调用,若内核数据未准备好,则应用程序一直阻塞等待,直至内核数据准备好,从内核拷贝到用户空间才返回成功,此次IO为阻塞IO
非阻塞IO
-
若内核数据未准备好则可以先返回错误信息给用户进程,时期通过轮询的方式进行请求
- 应用进程向OS内核发起recvfrom读取数据
- OS内核数据未准备好则返回EWOULDBLOCK错误码
- 应用成勋进程轮询调用,继续向OS内核发起recvfrom读取数据
- OS内核数据准备好后,从内核缓冲区拷贝到用户空间
- 完成调用,返回成功提示
-
非阻塞IO因为需要频繁的轮询,导致频繁的系统调用,同样会消耗大量的CPU资源,因此依然存在性能问题
IO多路复用
-
IO复用的核心思想是:系统提供一类函数(select poll epoll),其可以同时监控多个文件描述符操作(fd 当程序打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符),任何一个返回内核数据就绪,应用进程再发起recvfrom系统调用
-
IO多路复用-select
-
在select函数监控的fd中,只要有任何一个数据准备就绪,select函数就返回可读的状态,此时应用进程再发起recvfrom请求去读取数据
-
select的缺点
-
监听的IO最大连接数有限,在linux系统上一般是1024
-
select函数返回后,通过遍历fdset,找到就绪的描述符fd (仅知道有IO时间发生但不知道是哪几个流所以需要遍历所有流)
-
-
-
IO多路复用 - poll
- poll 与 select相比解决了连接数限制问题
- 但和select一样需通过遍历文件描述符获取已经就绪的sokect,若同时连接大量客户端,在一个时刻可能只有极少数处于就绪状态,伴随着监视的描述符数量的增长,效率也会线性下降
-
IO多路复用 - epoll
-
采用时间驱动实现
-
epoll通过epoll_ctl()注册一个fd, 一旦基于某个fd就绪,内核会采用回调机制迅速激活该fd,当进程调用epoll_wait()时便得到通知。因此epoll采用监听时间回调的机制减少了遍历文件描述符的操作
-
epoll明显优化了IO执行效率但是在进程调用epoll_wait()时仍然可能被阻塞,因此可使用 信号驱动IO模型等内核数据准备就绪主动通知
-
-
IO模型之信号驱动模型
-
应用进程请求IO时向内核发送一个信号(调用sigaction时建立一个SIGIO信号),当内核数据准备好后,通过SIGIO信号通知应用进程,应用进程收到该信号后调用recvfrom去读取数据
-
缺点:数据复制到应用缓冲的时候,应用进程还是阻塞的
-
-
IO多路复用 - 异步IO
-
应用进程发出系统调用后,立即返回,等内核数据准备好,将数据拷贝到用户进程缓冲区,发送信号通知用户进程IO操作执行完毕
-
异步IO仅需向内核发送一次请求就可以完成数据状态询问和数据拷贝所有操作,而不用阻塞等待结果
-
例子: 当发起一笔批量转账时(耗时操作),后端可以先告知前端转账提交成功,等结果处理完成,再通知前端结果即可
-
-
阻塞、非阻塞、同步、异步IO区分
select poll epoll的区别
-
select poll epoll 底层数据结构 数组 链表 红黑树和双链表 获取就绪的fd 遍历 遍历 事件回调 事件复杂度 O(n) O(n) O(1) 最大连接数 1024 无限制 无限制 fd数据拷贝 每次调用select,需要将fd数据从内核空间拷贝到用户空间 每次调用poll,需要将fd数据从内核空间拷贝到用户空间 使用内存映射(mmap),不需要从内核空间频繁拷贝fd数据到用户空间
ref:
https://mp.weixin.qq.com/s/bb7C6VNbq7REP9u8PsreSg