一、概述
阻塞与非阻塞的概念
在用户进程或者线程进行系统调用进入内核的时候,如果不能马上满足条件,内核对调用者采取不同的行为,分为“阻塞”和”非阻塞“两种。”阻塞“表示如果系统调用不能马上获得满足而返回,就将调用者(进程或线程)挂起,等到满足条件,内核执行完系统调用,重新恢复调用者。”非阻塞“表示如果系统调用不能满足,则立即返回,并设置错误信息以告知调用者,该系统调用因何不能满足。
从以上看,”阻塞“虽然使用比较简单,但是很被动,会出现”永久阻塞“而导致无法处理的问题,设想一下,客户端进程阻塞等待接受服务器的消息,但是这个服务器因为某种原因崩溃了,如此客户端就会永久阻塞。相反,”非阻塞“技术给了程序员更大的灵活性,只要能够合理处理各种情况便能够更好的提高效率。
总之,非阻塞调用相对与阻塞调用有几个好处:
- 不会出现“永久阻塞”(设置超时也可解决永久阻塞问题)
- 同时监听多个IO复用的程序模型,如果使用阻塞调用,可能出现顾此失彼的情况。即,系统调用阻塞在某个IO上时,可能有另一个IO已经就绪,而它不能被及时响应。
也因为如此,使用非阻塞技术要体现这些优势。
网络编程背景下的非阻塞式IO
可能阻塞的套接字调用分为以下四类:
- 输入操作(read、readv、recv、recvfrom、recvmsg五个函数)
- 输出操作(write、writev、send、sendto、sendmsg五个函数)
- 接受外来连接,accept函数
- 发起外出连接,用于TCP的connect函数
二、非阻塞读和写
UNP给出一个数据,echo服务下的客户端不同版本str_cli函数,测试从一个Solaris客户主机想RTT为175毫秒的一个服务器主机复制2000行文本,所花时间的不同:
- 354.0s,停等版本
- 12.3s,select+阻塞式IO版本
- 6.9s, select+非阻塞IO版本
- 8.7s, fork版本(多进程)
- 8.5s,多线程版本
从这个数据可以看出: - 一、IO复用模型的使用确实大大加快了IO的处理
- 二、IO复用模型搭配非阻塞技术可以再提高效率(但是会提高编程的复杂性)
UNP推荐:相对于select+非阻塞IO,最好使用效率差不多、但是编程更简单的多进程模型。
对UNP书上str_cli函数的select+非阻塞式IO的注解
本版本需要做的工作:
- 维护两个缓冲区;
- 使用selectIO复用,监听并处理四个IO;
- 对所有监听的IO都设置成非阻塞IO;
- 恰当处理终止条件,当服务器终止连接或者客户端标准输入得到EOF;
动态性强,但是编程要考虑的情况多。UNP提供了效率差不多,但是编程简单的fork版本。该版本做的工作较少:
- fork出子进程,负责阻塞等待套接字可读,并将收到的消息输出到标准输出;
- 父进程负责阻塞等待标准输入可读,并将收到的消息写入套接字;