高级IO知识点(select、poll、epoll)

1.什么是IO

IO就是input(输入)和output(输出)
IO的过程:

  1. 等待IO就绪,等待资源就绪(资源可用)
  2. 拷贝数据到缓冲区当中

2.五种IO模型

2.1 阻塞IO

介绍:在资源不可用的情况下,IO请求一直被阻塞,直到资源可用。
图解:
在这里插入图片描述
例子:当钓鱼的时候,将鱼钩甩入水中,眼睛一直看着鱼漂,直到鱼漂沉下去,再将鱼拉上来,钓鱼完毕。
特点

  1. 调用阻塞IO的方式,阻塞等待的时长取决于内核
  2. 在等待过程当中,程序与CPU的利用率是极低的
  3. 在IO等待就绪到拷贝数据之间,实时性特别高
  4. 代码调用简单,容易编写

2.2 非阻塞IO

介绍:在资源不可用的情况下,IO请求不会被阻塞,当发起一次IO调用之后,调用直接返回,返回资源不可用。基于这种情况,因为非阻塞IO直接调用返回的特性,导致了我们需要对调用结果进行判断,判断调用是否完成。因此,需要搭配循环来使用,循环的目的是防止调用没成功,需要二次调用,直到调用完毕。
图解
在这里插入图片描述
举例:钓鱼的时候,将鱼钩抛入水中,眼睛看一眼鱼漂,如果鱼漂没有动,则刷一会儿抖音,再看一眼鱼漂,如此往复,直到鱼漂动了,将鱼钓上来。
特点

  1. 相较于阻塞IO而言,CPU的利用率比阻塞IO要高
  2. 非阻塞IO增加了循环,导致代码复杂
  3. 数据就绪和拷贝实时性不高
  4. 循环调用

2.3 信号驱动IO

介绍:在代码当中自定义一个信号(SIGIO-29)处理函数,当IO准备就绪之后,进程就会收到SIGIO信号,回调自定义注册的函数,在自定义注册的函数内部发起IO调用。
图解
在这里插入图片描述
例子:钓鱼的时候,在鱼竿上绑一个铃铛,钓鱼人就可以忙其他事情了,当鱼竿上面的铃铛响起,只需将鱼竿拉起,完成钓鱼过程。
特点

  1. 代码更加复杂,牵扯到了信号
  2. 相比较非阻塞IO,IO准备就绪到拷贝数据之间更加实时
  3. 虽然加上信号逻辑之后,代码复杂了,但是我们不需要在代码当中循环调用判断了

2.4 多路转接IO

介绍:可以监控多个文件描述符,判断多个描述符是否准备就绪。
图解
在这里插入图片描述

2.4.1 select

原理:多路转接本质上就是将多个文件描述符放到内核当中,让内核进行监控,内核在监控多个文件描述符时候,一旦发现有文件描述符所对应的事件就绪,则返回该文件描述符,让用户执行流进行相应事件的处理。
接口

int select(int nfds, fd_set* readfds,  fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);
//nfds:内核遍历事件集合的范围,取决于我们传递给内核最大文件描述符,数值=最大文件描述符数值+1
//readfds:读事件集合
//writefds:写事件集合
//exceptfds:异常事件集合
//timeout:NULL表示阻塞监控;0表示非阻塞监控;带有超时时间监控

select的返回值:

  1. 如果监控的文件描述符有对应的事件产生,select返回值大于0,返回3,表示有3个文件描述符就绪
  2. 返回0,表示监控超时了
  3. 返回-1,表示监控出错了

fd_set事件集合本质上是一个位图
4. 位图的大小取决于内核当中_FD_SETSIZE这样一个宏
5. 当我们关心某一个文件描述符的某一个事件的时候,只需要将该文件描述符添加到对应的事件集合当中
6. 添加文件描述符到事件集合当中的时候,其实是将文件描述符对应的在事件集合当中的比特位置为1

在这里插入图片描述

void FD_CLR(int fd, fd_set* set);//将fd文件描述符从事件集合当中移除,本质上就是将文件描述符对应的比特位置0
int FD_ISSET(int fd, fd_set* set);//判断fd文件描述符是否在事件集合当中,本质上就是判断文件描述符所对应的比特位是否为1.如果当前比特位为0,表示不在,函数返回0;如果当前比特位为1,则表示在,函数返回1
void FD_SET(int fd, fd_set* set);//将文件描述符fd添加到事件集合当中,本质上就是将文件描述符对应的比特位设置成为1
void FD_ZERO(fd_set* set);//清空事件集合,将所有比特位设置为0

优点

  1. 遵循posix标准,可以跨平台使用,可以在win平台使用,也可以在linux平台使用
  2. select是超时时间可以精确到微妙

缺点

  1. select监控文件描述符的时候,采用轮询遍历的方式,随着监控的文件描述符越多,监控(轮询)效率越低
  2. select监控文件描述符的个数是有上限的,为1024
  3. select在返回就绪文件描述符的时候,会将未就绪的文件描述符从事件集合当中移除掉,导致二次监控的时候,需要自己再次手动添加
  4. 返回就绪文件描述符的时候,返回了一个事件集合,并不是将就绪文件描述符数值直接返回给调用者,需要调用者使用FD_ISSET函数进行判断

2.4.2 poll

接口

int poll(struct pollfd* fds, nfds_t nfds, int timeout);
//fds:事件结构数组
//nfds:指的是fds事件结构数组当中到底存在多少有效元素
//timeout:超时时间。大于0表示带有超时时间的监控;等于0表示非阻塞监控;小于0表示阻塞监控

返回值:
大于0表示有多少个文件描述符就绪
等于0表示超时
小于0表示监控出错

优点

  1. poll提出了事件结构这样的结构体,大大简化了代码
  2. 不限制文件描述符的个数,只要在传递事件结构数组的时候,对数组进行扩容就好
  3. poll函数在返回之后,不需要重新添加关心的文件描述符

缺点

  1. 随着监控文件描述符个数的增多,监控效率会下降(监控策略和select一样,都采用轮询遍历的方式进行监控)
  2. poll不支持跨平台
  3. poll在监控的时候,同样是将用户定义好的事件结构数组拷贝到内核当中,在监控成功之后,将事件结构数组再从内核当中拷贝到用户空间,这样比较耗费性能

2.4.3 epoll

epoll是当今世界上公认的在linux下性能最高的多路转接IO模型

接口

int epoll_create(int size);
//创建epoll句柄
//size:size在现在的内核源码当中已经被弃用掉了,本质上是设置epoll在内核当中对应结构体的大小,内核在采用动态开辟空间的方法去维护epoll对应的结构体大小;传参的时候,size不要传递小于0的值
//返回值:返回操作epoll在内核当中结构体的句柄

int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);
//epfd:epoll的操作句柄
//op:告诉函数做什么操作
//fd:程序员需要监控的文件描述符
//event:epoll监控的事件结构

int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);
//epfd:epoll操作句柄
//events:epoll事件结构数组
//maxevents:最大拷贝多少事件结构到events
//timeout:超时时间。大于0表示带有超时时间监控;等于0表示非阻塞监控;小于0表示阻塞监控
//返回值:大于0表示多少文件描述符就绪;等于0表示超时;小于0表示监控失败

2.5 异步IO

处理过程

  1. 自定义信号处理函数
  2. 发起IO调用

图解
在这里插入图片描述
例子:钓鱼的时候,将女朋友带着,让女朋友进行钓鱼,钓到鱼之后告诉我,我直接拿着鱼。

3.阻塞、非阻塞、同步、异步概念

阻塞和非阻塞
只需要判断在资源不可用的条件下,发起IO调用。如果IO调用不返回,则为阻塞;如果IO调用返回了,则为非阻塞,当前IO调用并没有完成功能,需要循环调用

同步和异步
只需要判断数据是谁拷贝完成的。如果数据是用户自己拷贝完成的,则为同步;如果数据为内核拷贝,则为异步。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值