参考: https://baijiahao.baidu.com/s?id=1718409483059542510&wfr=spider&for=pc

一次IO过程如下

  • 应用程序进程向操作系统发起IO调用请求
  • 操作系统准备数据,把IO外部设备的数据,加载到内核缓冲区
  • 操作系统拷贝数据,即将内核缓冲区的数据,拷贝到用户进程缓冲区

IO模型 - 图1

阻塞IO(BIO)

  • 如果内核数据内有准备好, 那么用户进程将一直阻塞, 浪费CPU资源
  • 应用: 阻塞socket、Java BIO

IO模型 - 图2

非阻塞IO(NIO)

  • 应用进程轮询调用系统内核
  • 频繁轮询依然消耗CPU资源

IO模型 - 图3

多路复用IO

select

  • 应用进程通过select函数询问内核数据是否准备就绪, 数据就绪后再发起系统调用
  • 缺点
    • 监听的IO最大连接数在Liunx上最大为1024
    • select函数返回后, 需要遍历所有流才能找到就绪的描述符fd, 如果同时连接大量的客户端, 同一时刻可能只有极少数流处于就绪状态, 随着监视的描述符数量增长, 效率也会线性下降
  • poll模型解决了连接数的限制, 但解决不了遍历所有流的问题

IO模型 - 图4

epoll

  • 采用监听事件回调机制, 先通过epoll_ctl()注册一个文件描述符fd, 一旦某个fd就绪时, 内核会采用回调机制, 迅速激活fd, 当进程调用epoll_wait()时便得到通知
  • 进程调用epoll_wait()时依然有可能被阻塞

IO模型 - 图5

select, poll, epoll 对比

IO模型 - 图6

信号驱动模型

  • 不再主动询问内核数据是否就绪, 而是向内核发送一个信号, 然后应用进程去做别的事, 不用阻塞, 当内核数据准备好后, 再通过信号通知应用进程, 应用进程收到信号立刻读取数据

IO模型 - 图7

异步IO(AIO)

  • 上面的BIO, NIO, 信号驱动等模型, 在数据从内核复制到应用缓冲的时候都是阻塞的, 异步IO实现了IO全流程的非阻塞, 在应用进程发出系统调用后, 立即返回成功, 等内核数据拷贝完成后再通知用户进程操作执行完毕

IO模型 - 图8

总结

IO模型 - 图9
IO模型 - 图10