IO 的两个阶段
- 数据准备阶段
- 数据拷贝阶段
1、阻塞 IO
linux 中,默认所有的 socket 都是阻塞的。
阻塞 IO
2、非阻塞 IO
数据准备阶段,非阻塞立即返回,未准备好返回-1。而数据拷贝阶段都是阻塞的。
非阻塞 IO
2.1、阻塞 IO 与 非阻塞 IO
两者的差异在于:IO 函数在数据准备阶段(数据未就绪时)是否立刻返回
int n = read(fd, buf, size) | write(fd, buf, size) // 1、阻塞IO,阻塞在网络线程 // 2、非阻塞IO,errno = EWOULDBLOCK,未发送完的数据注册写事件,等待下次写事件触发时发送 if (-1 == n) errno = EWOULDBLOCK
fd 默认是阻塞的,连接的 fd 阻塞属性决定了 IO 函数是否阻塞
// 设置非阻塞 IO int flag = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, O_NONBLOCK);
3、IO 多路复用
用一个线程来检测多个 IO 事件,作用于数据准备阶段。
IO 多路复用
应用程序通过 IO 复用函数向内核注册一组事件,内核通过 IO 复用函数把其中就绪的事件通知给应用程序,又称为事件驱动 IO。IO 复用函数本身是阻塞的,但可同时监听多个 IO 事件,对 IO 本身的读写操作是非阻塞的。
注:边缘触发的时候,IO 函数只能是非阻塞的。
4、信号驱动IO
信号触发读写就绪事件,用户信号处理函数完成读写操作,不阻塞。
信号驱动 IO
缺点:大量 IO 操作时,信号较多,SIGIO 处理函数不能及时处理可能导致信号队列溢出,而且内核空间与用户空间的频繁信号交互性能较低。
5、异步IO
同步 IO 向用户通知的是 IO 就绪事件,异步 IO 向用户通知的是 IO 完成事件,真正实现了非阻塞。
异步 IO
6、总结
- 阻塞 IO 与非阻塞 IO:数据准备阶段是否阻塞。
- 同步 IO 与 异步 IO:数据拷贝阶段是否同步。
IO 模型