本文已参与「新人创作礼」活动,一起开启掘金创作之路。
五种IO模型:
- 阻塞IO(
BIO) - 非阻塞IO(
NIO) - 信号驱动IO(
signal-driven I/O) - 异步IO(
AIO) - 多路转接(复用)IO(
I/O multiplexing)
IO操作分了两个过程:等待 + 数据拷贝。
-
阻塞
IO:发起IO调用,若不具备IO条件,则等待IO条件具备。具备则数据拷贝完毕后返回。一直等待资源浪费。 -
非阻塞
IO:发起IO调用,若不具备条件则立即报错返回。通常是循环发起调用,若具备IO条件,则拷贝数据完毕后返回。不够实时。 -
信号驱动
IO:先定义IO信号处理方式,若IO条件具备,直接信号通知进程,发起调用,拷贝数据后返回。比较实时。但流程控制较难。也是一种异步,因为拷贝就是异步的。 -
异步
IO:定义信号处理,发起异步IO调用,自己直接返回,之后让别人等待条件具备(等待和数据拷贝都不用自己完成,进程或线程完成)“事情由别人干,干完通知我”。IO条件具备后,数据拷贝由别人完成,信号通知进程:IO已经完成。可以对数据直接进行操作。(AIO) -
多路转接
IO:一种IO事件监控。同时对大量的描述符进行事件(描述符的可读/可写/异常)默认阻塞监控,监控描述符是否具备IO条件。如果具备(就绪时)进行返回,对就绪的IO进行操作。是高并发的处理模型。 多路转接模型:(select、poll、epoll)都是实现对大量描述符进行事件监控的操作。
select模型:
【通过对几个事件集合中的描述符进行各自的事件监控,当对应集合中有描述符事件就绪则返回,返回前集合中没有就绪的描述符则移除。否则超时返回】
int select( //阻塞函数
int maxfds ,
fd_set* readfds ,
fd_set* writefds ,
fd_set* exceptfds ,
struct timeval* timeout
);
maxfds:监控的描述符中,最大的描述符 + 1fd_set:描述符集合,本质是一个位图(bitmap),位图大小取决于一个宏:__FD_SETSIZE=1024,即最多监控1024个。readfds:监控读事件集合。writefds:监控写事件集合。exceptdfs:监控异常事件集合。struct timeval:分为秒与微秒。timeout:是select等待超时时间。
步骤:
- 定义描述符集合
fd_set。 - 将集合拷贝到内核进行监控,监控的原理是对所有描述符进行轮询遍历状态(这个监控是==阻塞==的)
- 当有描述符就绪时,在调用返回之前,将集合中没有就绪的描述符剔除出去。
- 用户操作:对所有的描述符进行遍历,看集合还剩余什么描述符,则这些描述符已经就绪。
接口:
void FD_CLR(int fd,fd_set *set); //将指定的描述符从集合中移除
void FD_ISSET(int fd,fd_set *set); //判断制定的描述符是否在集合中
void FD_SET(int fd,fd_set *set); //将指定的描述符添加到监控集合中
void FD_ZERO(fd_set *set); //清空描述符集合
select优缺点分析:
- 优点:
- 遵循
POSIX标准,可以跨平台。(移植性强) select监控的超时等待时间更加精细。(微秒级别)
- 缺点:
select所能监控的描述符是有上限的,Linux下默认1024,取决于__FD_SETSIZE。select实现监控原理是在内核中进行轮询遍历状态,因此性能会跟着描述符增多而下降。select监控每次返回时都会修改监控集合,需要用户每次监控前重新添加描述符到集合中。select要监控的集合中的描述符数据,需要每次都重新向内核中拷贝。select不会直接告诉用户哪一个描述符事件就绪,只是告诉用户有就绪事件,需要用户遍历查找。(会增加代码复杂度)