io多路复用


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
服务端:

  1. 在accept处阻塞等待客户端发起连接
  2. read:建立了一个链接后(经历了三次握手)在这里阻塞(等待)读取

客户端:
3. connect和write地方阻塞住

小结:在处理客户端1的时候,客户端2的链接没法进来,知道服务端进入下一次循环
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
4. 设置了非阻塞模式setnonblocking
5. 没有链接时候accept立刻会返回-1

在这里插入图片描述
用户空间——执行安全的指令
内核空间——特权指令
linux将socket封装之后提供read,write系统调用函数,调用的时候会切换到内核台;
read:数据从网卡拷贝到内核空间即socket缓冲区——再拷贝到用户空间;
write:用户空间拷贝到内核空间,再拷贝到网卡;

例子:
在这里插入图片描述

4个socket链接=4个fd;
用户空间调用某个fd的read:将fd拷贝到内核空间——内核会判断这个fd对应的socket是否有数据到达,对于同步非阻塞:如果没有就返回-1;
用户空间收到-1——检查下一个fd——有数据,就处理

在这里插入图片描述
优点:避免的单个socket阻塞
缺点:如何fd一直没有数据,就会不断的去遍历进行系统调用,涉及到用户态和内核态的切换,开销大

I/O多路复用

I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。select,poll,epoll都是IO多路复用的机制。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

I/O多路复用

select,poll,epoll

select

在这里插入图片描述
在这里插入图片描述
缺点4:因为入参到操作系统里面可能会被改成其他的值,所以在下次调用的时候得重置入参数
在这里插入图片描述

过程:

  1. 用户空间:所有fd拷贝到内核空间,并在select处阻塞住
  2. 内核空间:遍历所有fd,检查是否有数据(就绪),对就绪的打标,返回就绪的fd的数量
  3. 用户空间:知道就fd就绪了,就再次便利所有的fd,找到所有就绪的fd,并处理

问题:没有数据的时候怎么办?
4. 会在select处阻塞
5. 客户端发数据——数据通过网络传输到大服务端的网卡——网卡将数据写入到指定的内存——中断信号告诉cpu有新的数据到达——cpu响应中断,调用中断处理程序处理——根据数据包的ip和端口号找到socket——数据保存到socket的就绪队列——检查socket对应的等待队列中是否有进程在阻塞,有就唤醒——内核空间被唤醒继续检查fd

问题:select返回的是int,即多少个fd就绪,那么怎么返回哪些是就绪的呢?
在这里插入图片描述
通过入参的fd_set:位图实现;
入参:表示哪些fd是我要监听的(1来表示)
回参:哪些fd就绪了

poll

在这里插入图片描述
在这里插入图片描述
poll的优化:

  1. 用了一个新的数据结构,那么就不需要每次返回的时候重置了
  2. 虽然用户传的fd 是数组,但是内核空间是用链表的形式村粗监听的fd,而链表是没有限制的,所以突破了1024
    缺点:
    1、大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义;

2、与select一样,poll返回后,需要轮询pollfd来获取就绪的描述符。
3. poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。

优点:

1、poll() 不要求开发者计算最大文件描述符加一的大小。

2、poll() 在应付大数目的文件描述符的时候速度更快,相比于select。

3、它没有最大连接数的限制,原因是它是基于链表来存储的。

小结poll和select公共缺点:
3. 用户到内核台的切换,尤其是fd很大的时候
4. 不知道哪个fd就绪,用户态得重新遍历,On

epoll

在这里插入图片描述
在这里插入图片描述
wq:将进程阻塞的时候,会关联到这个等待队列,以便有事件来了,唤醒

epoll_ctl:
在这里插入图片描述

在这里插入图片描述

就绪队列空——将当前进程添加到等待队列
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

epoll

epoll来数据了:
客户端发数据——数据通过网络传输到大服务端的网卡——网卡将数据写入到指定的内存——中断信号告诉cpu有新的数据到达——cpu响应中断,调用中断处理程序处理——根据数据包的ip和端口号找到socket——通过socket找到回掉函数epollcallback数据保存到socket的就绪队列rdlist双向链表——检查socket对应的等待队列中是否有进程在阻塞,有就唤醒——内核空间被唤醒继续坚持就绪的双向链表是否有就绪事件

epoll的机制:

epoll_ create:
建了个红黑树:用于存储以后epoll_ ctl传来的socket外;
再建立一个list链表:用于存储准备就绪的事件.
epoll_ ctl:

  1. 将socket,fd拷贝到内核,并封装成epitem:
    rbn:关联红黑树;
    ffd:关联就绪链表rdlist
    ep:关联eventpoll对象
    pwqlist:等待队列关联回调函数epollcallback
  2. 注册回掉函数

epoll_ wait:
仅仅观察这个双向list链表里有没有数据即可。有数据就把双向链表里的拷贝到events给用户态,并且根据et/lt来决定是否删除链表节点,没有数据就sleep,等到timeout时间到后即使链表没数据也返回,或者等待事件来了被唤醒。所以,epoll_wait非常高效。

epoll的优点:

1、没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口);
2、效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数;
即Epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll。

3、 内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。

  1. epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。
  2. 通过就绪链表知道哪些是就绪的,不用用户空间去便利

epoll既然是对select和poll的改进,就应该能避免上述缺点。那epoll都是怎么解决的呢?在此之前,我们先看一下epoll和select和poll的调用接口上的不同,select和poll都只提供了一个函数——select或者poll函数。而epoll提供了三个函数,epoll_create,epoll_ctl和epoll_wait,epoll_create是创建一个epoll句柄;epoll_ctl是注册要监听的事件类型;epoll_wait则是等待事件的产生。

对于第一个缺点,epoll的解决方案在epoll_ctl函数中。每次注册新的事件到epoll句柄中时(在epoll_ctl中指定EPOLL_CTL_ADD),会把所有的fd拷贝进内核,而不是在epoll_wait的时候重复拷贝。epoll保证了每个fd在整个过程中只会拷贝一次。

对于第二个缺点,epoll的解决方案不像select或poll一样每次都把current轮流加入fd对应的设备等待队列中,而只在epoll_ctl时把current挂一遍(这一遍必不可少)并为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表)。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd(利用schedule_timeout()实现睡一会,判断一会的效果,和select实现中的第7步是类似的)。

epoll没有FD个数这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。

epoll的触发模式

epoll的触发模式

LT模式

当epoll_ wait检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll_wait时,会再次响应应用程序并通知此事件。只要这个fd还有数据可读,每次 epoll_wait都会返回它的事件,提醒用户程序去操作

ET模式

ET(边缘触发)模式中,它只会提示一次,直到下次再有数据流入之前都不会再提示了,无 论fd中是否还有数据可读。所以在ET模式下,read一个fd的时候一定要把它的buffer读光,也就是说一直读到read的返回值小于请求值,或者 遇到EAGAIN错误。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。

ET、LT模式的原理

当一个socket句柄上有事件时,内核会把该句柄插入上面所说的准备就绪list链表,这时我们调用epoll_ wait,会把准备就绪的socket拷贝到用户态内存,然后清空准备就绪list链表。
  最后,epoll_ wait检查这些socket,如果不是ET模式(就是LT模式的句柄了),并且这些socket上确实有未处理的事件时,又把该句柄放回到刚刚清空的准备就绪链表了。
  所以,非ET的句柄,只要它上面还有事件,epoll_ wait每次都会返回。而ET模式的句柄,除非有新中断到,即使socket上的事件没有处理完,也是不会次次从epoll_wait返回的。

ET模式优点:

如果采用EPOLL LT模式的话,系统中一旦有大量你不需要读写的就绪文件描述符,它们每次调用epoll_wait都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率.。而采用EPOLL ET这种边沿触发模式的话,当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你!!!这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符。

select、poll、epoll区别:

1、支持一个进程所能打开的最大连接数

select:单个进程所能打开的最大连接数有FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器上,大小就是3232,同理64位机器上FD_SETSIZE为3264),当然我们可以对进行修改,然后重新编译内核,但是性能可能会受到影响,这需要进一步的测试。
poll:poll本质上和select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的。
epoll:虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接,2G内存的机器可以打开20万左右的连接。

2、FD剧增后带来的IO效率问题

select:因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度慢的“线性下降性能问题”。

poll:同上

epoll:因为epoll内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll没有前面两者的线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。
3、 消息传递方式

select:内核需要将消息传递到用户空间,都需要内核拷贝动作

poll:同上

epoll:epoll通过内核和用户空间共享一块内存来实现的。

总结:

综上,在选择select,poll,epoll时要根据具体的使用场合以及这三种方式的自身特点。

1、表面上看epoll的性能最好,但是在连接数少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调。
select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。这就是回调机制带来的性能提升。

2、select低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善 select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把current往设备等待队列中挂一次,而epoll只要一次拷贝,而且把current往等待队列上挂也只挂一次(在epoll_wait的开始,注意这里的等待队列并不是设备等待队列,只是一个epoll内部定义的等待队列)。这也能节省不少的开销。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值