I/O多路复用

I/O多路复用使得程序能同时监听多个文件描述符,能够提高程序的性能。
Linux上实现多路复用的有 select poll epoll

1、IO模型

在这里插入图片描述
在这里插入图片描述
所以要采用多路转接技术

在这里插入图片描述

2、多路复用

多路复用的三种方式
在这里插入图片描述客户端操作服务器时就会产生这三种文件描述符(简称fd):writefds(写)、readfds(读)、和 exceptfds(异常)。select 会阻塞住监视 3 类文件描述符,等有数据、可读、可写、出异常或超时就会返回;返回后通过遍历 fdset 整个数组来找到就绪的描述符 fd,然后进行对应的 IO 操作。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

首先是用户区存在一个fd_set的标志位区reads,大小为1024.
然后利用FD_SET函数将用户态区的reads这段set对应的位置标志为1.
接着利用selcet函数,将这个fd_set从用户态拷贝到内核态,然后内核态开始遍历与处理。
//101+1 表示要读到文件描述符的最大位置加1的位置,这样才能保证读到最大的那个文件描述符
假如A、B发送了数据,说明只有A、B的文件描述符的内容为1,C、D置为0,然后内核态的数据拷贝到用户态中去,用户态再进行相关读写操作。

缺点:
在这里插入图片描述①由于是采用轮询方式全盘扫描,会随着文件描述符 FD 数量增多而性能下降。
②每次调用 select(),都需要把 fd 集合从用户态拷贝到内核态,并进行遍历(消息传递都是从内核到用户空间)。
③单个进程打开的 FD 是有限制(通过FD_SETSIZE设置)的,默认是 1024 个,可修改宏定义,但是效率仍然慢。
在这里插入图片描述基本原理与 select 一致,也是轮询+遍历。唯一的区别就是 poll 没有最大文件描述符限制(使用链表的方式存储 fd)。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
没有 fd 个数限制,用户态拷贝到内核态只需要一次,使用时间通知机制来触发。通过 epoll_ctl 注册 fd,一旦 fd 就绪就会通过 callback 回调机制(O(1))来激活对应 fd,进行相关的 io 操作。epoll 之所以高性能是得益于它的三个函数:

①epoll_create() 系统启动时,在 Linux 内核里面申请一个B+树结构文件系统,返回 epoll 对象,也是一个 fd。
②epoll_ctl() 每新建一个连接,都通过该函数操作 epoll 对象,在这个对象里面修改添加删除对应的链接 fd,绑定一个 callback 函数
③epoll_wait() 轮训所有的 callback 集合,并完成对应的 IO 操作

3、触发模式

epoll 有 epoll LT 和 epoll ET 两种触发模式,LT 是默认的模式,ET 是“高速”模式。
1️⃣LT 模式下,只要这个 fd 还有数据可读,每次 epoll_wait 都会返回它的事件,提醒用户程序去操作,只要有数据去就会去提示。
2️⃣ET 模式下,它只会提示一次,直到下次再有数据流入之前都不会再提示了,无论 fd 中是否还有数据可读。所以在 ET 模式下,read 一个 fd 的时候一定要把它的 buffer 读完,或者遇到 EAGAIN 错误。若是没有读完,必须写一个循环判断buffer是否读完。
举一个寄快递的例子:
首先先把一个快递放在蜂巢快递箱中,之后又一个快递,再次放进去,之后又一个快递,再放进去,放了三次。
如果是LT模式的话,每一次放进的时候都会有提示;ET模式下,放进去后就保持触发状态了就不会再提示,只会提示一次。
在这里插入图片描述
上面是边缘触发,必须循环使得数据被读完,ELSE是水平触发,每次都会触发,不用循环就会读完数据。
大块数据用边缘触发,小块数据用水平触发。

默认的工作方式是LT模式:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述需要设置一下边沿触发
在这里插入图片描述

4、为什么引入IO多路复用

为什么引入IO多路复用
1️⃣同步阻塞(BIO)
服务端采用单线程,当 accept 一个请求后,在 recv 或 send 调用阻塞时,将无法 accept 其他请求(必须等上一个请求 recv 或 send 完),无法处理并发。

服务器端采用多线程,当 accept 一个请求后,开启线程进行 recv,可以完成并发处理,但随着请求数增加需要增加系统线程,大量的线程占用很大的内存空间,并且线程切换会带来很大的开销,10000 个线程真正发生读写事件的线程数不会超过 20%,每次 accept 都开一个线程也是一种资源浪费。

2️⃣同步非阻塞(NIO)
服务器端当 accept 一个请求后,加入 fds 集合,每次轮询一遍 fds 集合 recv(非阻塞)数据,没有数据则立即返回错误,每次轮询所有 fd(包括没有发生读写事件的fd)会很浪费 cpu。

3️⃣IO 多路复用
服务器端采用单线程通过 select/epoll 等系统调用获取 fd 列表,遍历有事件的 fd 进行 accept/recv/send,使其能支持更多的并发连接请求。

老板拿快递,老板拿快递需要先查看快递(accept),然后在拿走快递(recv)
BIO:
一个老板时:老板在查看快递之后进入拿快递状态,此实别的快递老板查看不了,必须等待拿完后才能查看,就阻塞了,无法并发处理。
多个老板时:老板在查看快递的时候,可以找另一个老板去进行拿快递,这样可能会需要很多老板一起去工作,开销很大。
NIO:
老板查看过快递后,把快递丢在包里(这个包有很多个快递对应的位置),先不拿走,然后去查看每个位置中是否有需要拿的快递,如果有的话就统一拿走。这种方式,每次轮询这个包的位置时候耗费时间。
IO多路复用:
老板找了个员工(系统调用)去查看快递,然后自己对快递进行拿。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值