原标题:网络IO模型实现的方式有哪些?
网络IO模型实现的方式有哪些?
IO 是计算机体系中重要的一部分 。不同的 IO 设备有着不同的特点:数据率不一样、传送单位不一样,数据表示不一样,等等。所以,很难实现一种统一的输入输出方法。IO 有两种操作,同步 IO 和异步 IO。
同步 IO 指的是,必须等待 IO 操作完成后,控制权才返回给用户进程。
异步 IO 是,无须等待 IO 操作完成,就将控制权返回给用户进程。
下面就来给大家学习一下常见的几种IO模型。
一、阻塞IO模型
在Linux ,默认情况下所有的 socket 都是阻塞的,一个典型的读操作流程如图所示。
阻塞和非阻塞的概念描述的是用户线程调用内核 IO 操作的方式:阻塞是指 IO 操作需要彻底完成后才返回到用户空间;而非阻塞是指 IO 操作被调用后立即返回给用户一个状态值,不需要等到 IO 操作彻底完成。
当应用进程调用了 recvfrom 这个系统调用后,系统内核就开始了 IO 的第一个阶段 :准备数据。
当系统内核一直等到数据准备好了,它就会将数据从系统内核中拷贝到用户内存中,然后系统内核返回结果,用户进程才解除阻塞的状态,重新运行起来。所以,阻塞 IO 模型的特点就是 IO 执行的两个阶段都被阻塞了。实际上,除非特别指定,几乎所有的 IO 接口都阻塞型的。这给网络编程带来了一个很大的问题,如在调用 send 的同时,线程处于阻塞状态,则在此期间,线程将无法执行任何运算或响应任何网络请求。
二、非阻塞IO模型
在Linux 下,可以通过设置 socket IO 变为非阻塞状态。当一个非阻塞的 socket执行 read 操作时,流程如图:
在非阻塞状态下,recv 接口在被调用后立即返回,返回值代表了不同的含义,如下所述。
recv 返回值大于 0,表示接收数据完毕,返回值即是接收到的字节数。
recv 返回 0,表示连接已经正常断开。
recv 返回 -1 ,且 errno 等于 EAGAIN ,表示 recv 操作还没执行完成。
recv 返回 -1,且 errno 不等于 EAGAIN ,表示 recv 操作遇到系统错误 errno。
此外,在这个方案 recv 更多的是起到检测“操作是否完成”的作用,实际操作系统提供了更为高效的检测“操作是否完成”作用的接口,例如 select 多路复用模式,可以次检测多个连接是存活跃。
三、多路IO复用模型
多路 IO 复用,有时也称为事件驱动 IO。它的基本原理就是有个函数会不断地轮询所负责的所有 socket ,当某个 socket 有数据到达了,就通知用户进程IO 复用模型的流程如图:
当用户进程调用了 select ,那么整个进程会被阻塞,而同时,内核会 "监视" 所有 select 负责的 socket ,当任何一个 socket 中的数据准备好了, select 就会返回。这个时候用户进程再调用 read 操作,将数据从内核拷贝到用户进程。
四、异步IO模型
异步 IO 模型如图。
用户进程发起 read 操作之后,立刻就可以开始去做其他的事;而另一方面,从内核的角度,当它收到一个异步的 read 请求操作之后,首先会立刻返回,所以不会对用户进程产生任何阻塞。
然后,内核会等待数据准备完成,然后将数据拷贝到用户内存中,当这一切都完成之后,内核会给用户进程发送一个信号,返回 read 操作已完成的信息。
调用阻塞 IO 一直阻塞住对应的进程直到操作完成,而非阻塞 IO 在内核还在准备数据的情况下会立刻返回。两者的区别就在于同步 IO 进行 IO 操作时会阻塞进程。
非阻塞 IO 在执行 recvfrom 这个系统调用的时候,如果内核的数据没有准备好,这时候不会阻塞进程。但是当内核中数据准备好时,recvfrom 会将数据从内核拷贝到用户内存中,这个时候进程则被阻塞。
而异步 IO 则不样,当进程发起 IO 操作之后,就直接返回,直到内核发送一个信号,告诉进程 IO 已完成,则在这整个过程中,进程完全没有被阻塞。
经过上面的学习,非阻塞 IO 和异步 IO 的区别还是很明显的。在非阻塞 IO 中,虽然进程大部分时间都不会被阻塞,但是它仍然要求进程去主动检查,并且当数据准备完成以后,也需要进程主动地再次调用 recvfrom 来将数据拷贝到用户内存中。而异步IO 则完全不同,它就像是用户进程将整个 IO 操作交给了内核完成,然后内核做完后发信号通知。
IO 作为计算机的基础知识,后台开发务必要掌握。更多网络编程相关的知识可以去学习 unix网络编程,最后祝大家学习愉快!