网络编程中常见的5种I/O模型

I/O模型


Unix下共有五种I/O模型:

1>:阻塞I/O

2>:非阻塞I/O

3>:I/O多路复用

4>:信号驱动I/O

5>:异步I/O


其中前四种是同步I/O模型,只有第五种是异步的。


同步与异步:

这里的同步和两个实体之间通信中的同步的概念是不一样的,这里的同步是指关于这个I/O中的一系列动作都需要自己来完成,无论你是原地等待事件的发生(阻塞)还是当某个事件已经准备好的时候你去完成后面的的动作(非阻塞)都属于同步。

异步,它是指是调用另一个执行者去完成,当执行者发现要处理的时间后调用你,你再完成这件事情,执行的过程和你的动作是不牵扯的。


阻塞与非阻塞:

阻塞是指,等待某个事件的发生,如果它没有发生则一直等待下去直到事件发生为止。

非阻塞,不需要死死的等待,当它有返回的时候再去处理。



1>阻塞I/O

使用的I/O函数,导致程序阻塞,等待数据,如果数据没有准备好则一直等待。

例如我们使用的阻塞式的socket创建套接字的时候,调用accept/recvfrom/connect...等都会是阻塞式的等待连接,请求或者数据的到来

阻塞式的函数实现起来很简单,但它有很多的问题,首先作为一个网络的服务器,阻塞式的等待会浪费大量的资源和时间,当服务器调用函数sendto给远端的一个客户端发送数据时,这时候不做其他的事情那么往后的动作都堵塞在后面,这样显然是不合适的,解决这种问题的方法往往是创建一个进程或者线程去解决这件事,因为进程的开销远远地大于线程的开销,所以使用多线程的方法解决是很行得通的。

阻塞式的I/O的好处就是它非常的稳定,他能保证连接保证接受和发送数据的确定性,这是大多大的互联网公司使用多线程的原因,并且使用线程池也会大大增加线程开启的效率。


2>非阻塞式I/O

调用socket函数并将其设置为非阻塞模式,设置方式,linux下调用fcntl()函数。


非阻塞方式下,当前执行流不再以睡眠的方式来等待请求或者数据的到来,而采用轮询的方式,这种方式的运作流程是这样的,每隔一个时间段便返回一次,如果有数据到来则返回,如果没有,则返回一个错误码WSAEWOULDBLOCK,我们需要不断的用循环来等待数据的成功返回,这个过程往往会占用大量的CPU。

非阻塞的缺点,虽然他不用再使等待太僵硬,但它不可避免的一次只能等待一个事件的到来。



3>信号驱动I/O

在TCPsocket中发生以下事件均会产生SIGIO信号,因为在同一个时候产生这种信号的原因太多我们不能区分到底是哪一种情况产生的SIGIO信号,所以在TCP中信号驱动是不太适合使用的,相反在UDP中却比较适合使用。

网络编程中常见的5种I/O模型_第1张图片


信号驱动的工作方式如下图所示


4>I/O多路复用。


这种方式下的工作方式就比较特别了,按TCPsocket来举例,他会当listen到一个新的链接的时候将它放到一个集合中,当这个集合中的套接字有读的情况,我们便读取,有写的情况,我们便写,它是非阻塞I/O和信号驱动I/O再加上可以等待多个,这三个的合体,常见的函数有select,poll,epoll等,这些函数会在后面的博客中细细讲述。

它的缺点就是,无论是上面所述的哪种函数,每次发送或者接受一个数据都会进入两次内核与用户的转换。

I/O多路复用的模型如下图




5>异步I/O

当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者的输入输出操作

网络编程中常见的5种I/O模型_第2张图片




5种I/O模型的对比如下图

网络编程中常见的5种I/O模型_第3张图片


可以看出最好的方式是异步I/O了当然它实现起来比较负责,阻塞I/O的可靠性最好。


你可能感兴趣的:(服务器,执行者,网络编程)