定义
阻塞是进程的一种状态,表示等待某个事件发生(如读写操作需要等待数据),进程阻塞就表示暂停执行了,和挂起类似。
- 阻塞IO:称阻塞的文件描述符为阻塞IO
- 非阻塞IO:称非阻塞的文件描述符为非阻塞IO
see: Linux高性能服务器编程 游双 P126
区别
阻塞IO
针对阻塞IO的系统调用(如read, write)可能因为无法完成而被系统挂起,直到等待的事件发生为止。举例:read和write,通常IO操作都是阻塞I/O的,也就是说当你调用read时,如果没有数据收到,那么线程或者进程就会被挂起,直到收到数据。
坏处:see: I/O多路复用技术(multiplexing)是什么? - 用心阁的回答 - 知乎
https://www.zhihu.com/question/28594409/answer/74003996
由于服务器可能要处理很多的连接,对于大部分的连接来说都是阻塞的,采用阻塞IO的模式可能会导致线程或者进程之间频繁切换,造成很大的系统开销。举个例子:当服务器需要处理1000个连接的的时候,而且只有很少连接忙碌的,那么会需要1000个线程或进程来处理1000个连接,而1000个线程大部分是被阻塞起来的。由于CPU的核数或超线程数一般都不大,比如4,8,16,32,64,128,比如4个核要跑1000个线程,那么每个线程的时间槽非常短,而线程切换非常频繁。这样是有问题的:
- 线程是有内存开销的,1个线程可能需要512K(或2M)存放栈,那么1000个线程就要512M(或2G)内存。
- 线程的切换,或者说上下文切换是有CPU开销的,当大量时间花在上下文切换的时候,分配给真正的操作的CPU就要少很多。
PS :实际上可能不需要这么多的线程,可以采用线程池的模式。
如上图所示,read直到数据复制到应用进程的缓冲区或者发生错误才会返回,这就是阻塞的定义:等待某个事件。
非阻塞IO
针对非阻塞IO的系统调用总是立刻返回,不管事件是否发生,如果事件没有立刻发生就返回-1,并且设置errno,就类似于出错了。对于accept,recv 和 send,事件未发生时,errno 通常被设置成 EAGAIN。
好处:如果等待的事件没有发生,如上图中所示的read数据未准备好,系统调用会立即返回而不是导致调用进程被挂起。
坏处:用户进程需要采用轮询的方式(逐个查找)查看事件状态,这往往耗费大量的CPU资源。
参考资料
Linux高性能服务器编程 游双
I/O多路复用技术(multiplexing)是什么? - 用心阁的回答 - 知乎
https://www.zhihu.com/question/28594409/answer/74003996