Linux中的IO模型(上):五种IO介绍、select模型

143 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。


五种IO模型:

  1. 阻塞IO(BIO
  2. 非阻塞IO(NIO
  3. 信号驱动IO(signal-driven I/O
  4. 异步IO(AIO
  5. 多路转接(复用)IO(I/O multiplexing

IO操作分了两个过程:等待 + 数据拷贝

  1. 阻塞IO:发起IO调用,若不具备IO条件,则等待IO条件具备。具备则数据拷贝完毕后返回。一直等待资源浪费。

  2. 非阻塞IO:发起IO调用,若不具备条件则立即报错返回。通常是循环发起调用,若具备IO条件,则拷贝数据完毕后返回。不够实时。

  3. 信号驱动IO:先定义IO信号处理方式,若IO条件具备,直接信号通知进程,发起调用,拷贝数据后返回。比较实时。但流程控制较难。也是一种异步,因为拷贝就是异步的。

  4. 异步IO:定义信号处理,发起异步IO调用,自己直接返回,之后让别人等待条件具备(等待和数据拷贝都不用自己完成,进程或线程完成)“事情由别人干,干完通知我”。IO条件具备后,数据拷贝由别人完成,信号通知进程:IO已经完成。可以对数据直接进行操作。(AIO

  5. 多路转接IO:一种IO事件监控。同时对大量的描述符进行事件(描述符的可读/可写/异常)默认阻塞监控,监控描述符是否具备IO条件。如果具备(就绪时)进行返回,对就绪的IO进行操作。是高并发的处理模型。 多路转接模型:(selectpollepoll)都是实现对大量描述符进行事件监控的操作。

select模型: 【通过对几个事件集合中的描述符进行各自的事件监控,当对应集合中有描述符事件就绪则返回,返回前集合中没有就绪的描述符则移除。否则超时返回】

int select(			//阻塞函数
	int maxfds ,
	fd_set* readfds ,
	fd_set* writefds ,
	fd_set* exceptfds ,
	struct timeval* timeout
);
  • maxfds:监控的描述符中,最大的描述符 + 1
  • fd_set:描述符集合,本质是一个位图(bitmap),位图大小取决于一个宏:__FD_SETSIZE = 1024,即最多监控1024个。
  • readfds:监控事件集合。
  • writefds:监控事件集合。
  • exceptdfs:监控异常事件集合。
  • struct timeval:分为秒与微秒。
  • timeout:是select等待超时时间。

步骤:

  1. 定义描述符集合fd_set
  2. 将集合拷贝到内核进行监控,监控的原理是对所有描述符进行轮询遍历状态(这个监控是==阻塞==的)
  3. 当有描述符就绪时,在调用返回之前,将集合中没有就绪的描述符剔除出去。
  4. 用户操作:对所有的描述符进行遍历,看集合还剩余什么描述符,则这些描述符已经就绪。

接口:

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优缺点分析:

  • 优点:
  1. 遵循POSIX标准,可以跨平台。(移植性强)
  2. select监控的超时等待时间更加精细。(微秒级别)
  • 缺点:
  1. select所能监控的描述符是有上限的,Linux下默认1024,取决于__FD_SETSIZE
  2. select实现监控原理是在内核中进行轮询遍历状态,因此性能会跟着描述符增多而下降
  3. select监控每次返回时都会修改监控集合,需要用户每次监控前重新添加描述符到集合中。
  4. select 要监控的集合中的描述符数据,需要每次都重新向内核中拷贝
  5. select不会直接告诉用户哪一个描述符事件就绪,只是告诉用户有就绪事件,需要用户遍历查找。(会增加代码复杂度)