参考:https://www.cnblogs.com/liushui-sky/p/12917347.html

介绍:

linux 的五种IO模型:阻塞IO、非阻塞IO、异步IO、信号驱动IO、多路复用IO
其中,阻塞、非阻塞、信号驱动、多路复用都属于同步IO

同步IO与异步IO

同步IO:应用程序向内核发起查询数据,如果有数据则将数据从内核copy到用户空间。
异步IO:应用程序向内核发送读数据请求,需要:
(1) 告诉内核数据存放位置
(2) 注册回调函数,当内核完成数据copy后调用回调通知应用程序读取数据。
同步与异步最大的区别:
同步IO数据从内核空间到用户空间的copy动作是由应用程序自己完成。
异步IO则是注册回调函数并告知内核用户空间缓冲区存放地址,数据copy由内核完成。

阻塞IO模型

介绍:
当应用程序向系统内核发起数据请求,此时应用程序处于一个阻塞状态,等待数据准备,内核系统等待数据准备完毕后,copy数据到用户空间,等待copy完成后,返回结果,然后再由应用程序继续往下执行(处理数据)。
上述过程,内核会去查看数据是否就绪,如果没有就绪,就会等待就绪,而用户线程就会处于阻塞状态,用户线程会交出CPU。
流程图:
image.png

非阻塞IO模型

介绍:
当用户线程发起一个IO操作后,并不等待,而是马上就会得到一个结果,如果结果是error时,证明数据还没准备好,于是它便会再次发送IO请求,直到数据准备完成。
在非阻塞IO模型中,用户线程需要不断的询问内核数据是否准备就绪,也就是说非阻塞IO不会交出CPU,而会一直占用CPU,这将会导致CPU占用率过高。
流程图:
image.png

异步IO模型

介绍:
当用户线程发起请求后,内核会立刻返回,此时用户线程就可以去做其他事情了,当数据准备就绪后,内核会发出信号,通知线程,数据已经准备完毕,此时用户线程就可以去处理数据了。
应用程序请求内核读取数据后不等待,执行其它事项,内核待数据就绪后直接拷贝到用户空间,并发送信号给应用程序,应用程序收到信号后处理数据。
流程图:
image.png

信号驱动IO模型

介绍:
在信号驱动IO模型中,当用户线程发起一个IO请求操作,会给对应的socket注册一个信号函数,然后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接收到信号之后,便在信号函数中调用IO读写操作来进行实际的IO请求操作。这个一般用于UDP中,对TCP套接口几乎没用,因为该信号产生过于频繁,并且该信号没有告诉我们发生了什么事情。
在UDP上,SIGIO信号会在下面两个事件的时候产生:
(1) 数据报达套接字
(2) 套接字上发生错误
流程图:
image.png

多路复用IO模型

介绍:
多路IO复用是操作系统级别的,所谓IO多路复用机制,就是通过一种机制,可以监听多个描述符,一旦描述符就绪(一般是读或写就绪),能够通知程序进行相应的读写操作,这种机制的使用需要额外的功能来配合:select、poll、epoll
select: 时间复杂度O(n),它仅仅知道了有I/O时间发生,却不知道是第几个流,只能进行无差别的轮循,找出能读出数据或能写入数据的流,对它们进行操作。
poll: 时间复杂度O(n),本质上与select无差别,它将用户传入的数据拷贝到内核空间,然后查询每个FD对应的状态,但是它没有最大连接数限制,原因是它是基于链表来存储的。
epoll: 时间复杂度O(1),epoll可以理解为event poll,它是通过事件驱动的,每个事件关联上fd。
流程图:
image.png

总结

阻塞IO与非阻塞IO:
这是最简单的模型,一般配合多线程来实现。
异步IO:
高效主流的模型,效率很高。
信号驱动IO:
一种同步IO,更加灵活
多路复用IO(select/poll/epoll):
一个现成解决多连接的问题