《unix网络编程》(11)tcp服务器的几种常见状况分析的“服务器进程终止”提到客户阻塞于fgets所以没办法收到服务器发的fin,只有当客户再次输入文本并发送给服务器后才会从套接字中读取,这时才知道服务器的状态。但这可能已经过了很长时间。这样的进程就需要预先告知内核的能力,使得内核一旦发现进程指定的一个或多个i/o条件就绪(即,输入已经准备好读取,或描述符能够承接更多输出),它就通知进程。这种能力就是i/o复用(i/o mutiplexing)。该能力由select和poll支持。
阻塞式i/o模型
最流行的是这种模型。
目前为止的例子都使用该模型。
默认情况下,套接字都是该模型。
使用udp为例(tcp复杂,还涉及套接字低水平标记等)是因为它简单:数据报要么收到,要么没有。进程调用recvfrom,其系统调用直到数据报达到且被复制到应用进程或者发生错误才返回。进程从调用recvfrom到它返回的整段时间都是被阻塞的。返回后,进程开始处理数据报。
非阻塞式i/o模型
设置为非阻塞是在通知内核:当所请求的i/o操作非得把本进程投入睡眠时,不睡眠,而是返回错误。前三次调用recvfrom时没数据报,内核立刻返回ewouldblock错误。第四次数据准备好,被复制到进程缓冲区,recvfrom成功返回,接着处理数据。这里不断查看某操作是否就绪,称为轮询,耗费大量cpu时间。一般是专门提供某一种功能的系统才用。
i/o复用(select、poll)
阻塞于select等待套接字可读,当select返回套接字可读时,调用recvfrom把数据读并复制到进程缓冲区。
i/o复用优势在于等待多个描述符就绪。
与此模型密切相关的是:在多线程下用阻塞式i/o(每个线程可自由调用如recvfrom之类的阻塞式i/o系统调用)。
信号驱动i/o模型
在描述符就绪时发送sigio信号通知我们。该模型优势是在等待数据报到达期间进程不阻塞;知道信号处理函数通知数据准备好。
异步i/o模型
posix规范定义的。
告知内核启动某操作,并让内核在整个操作(包括数据从内核复制到进程缓冲区)完成才通知我们。
与信号驱动i/o主要区别是:前者是内核通知我们何时启动一个i/o操作,而后者是由内核通知何时i/o操作完成,信号在操作完成才产生。
各种模型比较
前四种都是同步的,因为真正的i/o操作会阻塞进程;第五种是异步的。