Unix/Linux 上的五种IO模型
阻塞IO(blocking)
进程(或线程)阻塞于read:
应用程序read --- 系统调用 ---> 内核数据未准备好 --- 等待数据 ---> 数据准备好,拷贝数据 --- 数据从内核空间(TCP数据缓冲区)拷贝到用户空间(buf) ---> 拷贝完成 ---返回---> 应用处理数据
非阻塞IO(non-blocking) setsockopt(sockfd)
进程(或线程)调用read,判断EAGAIN反复调用:
应用程序read --- 系统调用 ---> 内核数据未准备好 --- size == 0 && errno == EAGAIN(等待数据) ---> 数据准备好,拷贝数据 --- 数据从内核空间拷贝到用户空间 ---> 拷贝完成 ---返回 ---> 应用处理数据
IO复用(IO multiplexing)
进程(或线程)阻塞于select/poll/epoll,等待套接字变为可读:
select/poll/epoll ---系统调用---> 内核数据未准备好 ---等待数据--->数据准备好 --->返回可读fd
可以通过设置select/poll/epoll/的timeout参数,这个阶段也可以变成非阻塞
阻塞于read读数据
前面数据准备好,返回可读fd ---> read(根据返回fd确定IO接口) ---系统调用 ---> 拷贝数据(数据从内核空间拷贝到用户空间) ---> 拷贝完成 ---返回---> 应用处理数据
select/poll/epoll(都是同步IO)
信号驱动(signal-driven)
进程继续执行:
应用程序注册SIGIO的信号处理程序 ---系统调用sigaction---> 内核数据未准备好 <---返回注册状态 ---等待数据---> 数据准备好 --- 通知SIGIO---> 执行信号处理程序
阻塞于read读数据
read ---系统调用 ---> 拷贝数据(数据从内核空间拷贝到用户空间) ---> 拷贝完成 ---返回---> 应用处理数据
内核在第一个阶段是异步,在第二个阶段是同步;与非阻塞IO的区别在于它提供了消息通知机制,不需要用户进程不断地轮询检查,减少了系统API的调用次数,提高了效率
异步IO(asynchronous)
进程继续执行:
应用进程aio_read ---系统调用 ---> 内核数据未准备好 <--- 返回调用状态 --- 等待数据 ---> 数据准备好 ---> 拷贝数据 ---> 数据从内核空间拷贝到用户空间(aio_cbp -> aio_buf) ---> 拷贝完成 --->aio_read注册的信号通知 ---> 应用处理数据