前面我们所使用的IO都是阻塞式的, 当然, 默认也是阻塞式的. 不过我们也可以设置文件描述符将阻塞式变为非阻塞式. 我们为什么会将阻塞式转为非阻塞式呢? 肯定是有好处的, 而本节就先简单介绍一下关于非阻塞式.
阻塞式
先来了解一下阻塞式.
阻塞式 : 进程运行到阻塞式的函数时会被投入睡眠切换执行其他程序, 直到被中断或者被唤醒才会切换回程序继续执行.
阻塞式的问题 :
- 对于CPU来说IO操作是最耗费时间的, 而等待的过程中并不能(或者不能高效)的做其他处理操作, 导致CPU的利用率低下. 在通信过程中, 基本上过半的时间都被IO阻塞, 而CPU在这段时间都是空闲无所事事的.
- 上下文切换耗时. 投入睡眠后将重新加载其他程序, 唤醒后又必须重新加载回来. 每次睡眠的时间如果每次都很短, 结果导致大部分时间都用来上下文切换, 而CPU并没有高效的被利用.
采用非阻塞式可以使程序不会投入睡眠区等待也不用上下文切换, 而是继续执行后续的指令, 充分利用CPU.
非阻塞式IO
先来介绍一下四种阻塞式IO.
-
输入操作 ( 如 : read, readv, recv, recvfrom, recvmsg )
TCP 和 UDP 套接字 :-
阻塞 : 如果接收缓冲区为空, 则调用会导致调用的进程休眠, 直到有数据后被唤醒.
-
非阻塞 : 函数立即返回(返回-1), 并置 errno 为
EAGAIN
后继续执行.
-
-
输出操作 ( 如 : write, writev, send, sendto, sendmsg )
阻塞 :
-
TCP : 如果发送缓冲区满了, 调用进程将会被阻塞.
-
UDP : 没有发送缓冲区,但可能会因为其它原因而阻塞.
非阻塞 : 函数立即返回(返回-1), 并置errno 为
EAGAIN
后继续执行. -
-
accept
函数- 阻塞 : 如果没有新的连接
- 非阻塞 : 函数立即返回(返回-1), 并置errno 为
EAGAIN
后继续执行.
-
connect
函数- 阻塞 :
connect
函数要对端确认 ACK 后才返回(成功/失败). - 非阻塞 :
connect
函数发送 SYN 后立即返回(失败), 并置errno 为EINPROGRESS
, 后续通过某些操作判断是否连接成功.
- 阻塞 :
注意 : 非阻塞可以避免程序睡眠和切换的好处, 但也使代码编写更加复杂.
非阻塞式IO的系统调用总是立即返回, 不管是否执行成功(读取倒数据).
小结
- 阻塞式问题
- 非阻塞式情况
- 非阻塞式问题