IO多路复用

IO多路复用

IO多路复用就是服务端用来处理大量客户端同时连接的情况,select,poll,epoll都是IO多路复用的机制

IO多路复用

何为IO多路复用?

IO多路复用就是一个线程或者一个进程同时监视多个文件描述符,一旦某个或某几个文件描述符准备就绪(读写就绪),就通知程序进行相应的读写操作。所以,这里的多路是指有多个网络连接,即多个客户端同时请求的情况,复用:复用同一个线程来监视所有网络连接,一旦某个或者某几个连接读写准备就绪,则通知应用程序进行读写

IO多路复用解决了什么问题?

没有IO多路复用机制时,有BIO、NIO两种实现方式,但有一些问题

  • BIO(同步阻塞IO)
    • 如果服务端是单线程:当一个线程执行accept一个请求后,在recv或send调用阻塞时,将无法accept其他请求(必须等上一个请求处recv或send完),无法处理并发
    • 如果服务端是多线程:当accept一个请求后,开启线程进行recv,可以完成并发处理,但随着请求数增加需要增加系统线程,大量的线程占用很大的内存空间,并且线程切换会带来很大的开销,10000个线程真正发生读写事件的线程数不会超过20%,每次accept都开一个线程也是一种资源浪费
  • NIO(同步非阻塞IO)
    • 服务端accept后,将所有请求的客户端或者网络连接都加入到fds文件描述符集合,每次轮询这个集合,有数据则进行读写,无则立马返回错误,每次都需要轮询这个集合

什么场景需要使用IO多路复用?

  • 当服务器需要处理多个socket连接
  • 当服务器既然处理TCP连接,又要处理UDP连接
  • 如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。

与多线程多进程相比,IO多路复用的最大优势就是复用一个线程或者一个进程,系统开销小,也不必对多线程多进程进行维护。

操作系统知识补充

用户态和内核态

现在操作系统都采用虚拟存储器,对于32位操作系统而言,他的寻址空间就是4G(2的32次方),操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限,为了保护应用程序不直接操作内核,保证内核安全,操作系统将虚拟地址空间划分为两部分:

  • 内核空间(内核态)

  • 用户空间(用户态)

针对linux操作系统而言,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为内核空间,而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为用户空间

进程切换

进程的上下文切换是在内核完成的,过程如下:

  • 保存处理器上下文,包括程序计数器和其他寄存器
  • 更新进程PCB信息
  • 把进程的PCB移入相应的队列,如就绪队列或者等待事件完成的阻塞队列
  • 选择另一个进程执行,并更新其PCB
  • 恢复处理机上下文
文件描述符FD

文件描述符就是一个用于描述指向文件的引用的抽象画概念

文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。

IO模型

数据到达的路径:

  • 数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。
BIO

等待数据和数据拷贝的过程中,这两个过程用户程序都是阻塞的

优点:

  • 能够及时返回数据,无延迟

缺点:

  • 对用户来说处于等待就要付出性能的代价了输入图片说明
NIO

非阻塞的recvfrom系统调用之后,进程并没有被阻塞,内核马上返回给进程,如果数据还没准备好,此时会返回一个error。进程在返回之后,可以处理其他业务逻辑,然后以轮询的方式发起recvfrom系统调用,直到数据准备好,再拷贝数据到进程,进行数据处理,但是数据拷贝的过程用户进程还是阻塞的,所以NIO最大的特点就是不断主动轮询内核,数据准备好了吗

优点:能够在等待任务完成的时间里干其他活了(包括提交其他任务,也就是 “后台” 可以有多个任务在同时执行)。

缺点:任务完成的响应延迟增大了,因为每过一段时间才去轮询一次read操作,而任务可能在两次轮询之间的任意时间完成。这会导致整体数据吞吐量的降低。而且轮询也消耗CPU时间

输入图片说明

AIO

用户程序进行aio_read系统调用之后,无论内核数据是否准备好,都会直接返回给用户进程,然后用户态程序继续执行其他业务逻辑,等到数据准备完成且数据拷贝完成后,内核向进程发送通知,这时候应用程序才开始处理IO读写,AIO中,数据准备和数据拷贝这两个过程都不会阻塞应用程序。

通知方式:

  • 信号
    • 在linux中是以信号通知的方式进行通知
  • 有一个专门执行回调通知的线程

输入图片说明

多路复用模型

IO多路复用有两个特别的系统调用函数select,poll,epoll,这三个函数可以为我们监听是否有socket数据准备好,只有有1个以上准备好就立马返回,用户进程在等待数据拷贝完成后就可以进行读写

当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。数据拷贝的过程,用户进程也是阻塞的,拷贝完成后,即可进行数据读写。

输入图片说明

**多路复用的特点:**就是通过一个线程或者一个进程同时监视/等待多个IO文件描述符,只要有一个以上数据准备完成,即可返回。监视的方式分为:

  • select
  • poll
  • epoll

但是千万别以为IO多路复用一定比BIO或者NIO优秀:

  • IO多路复用往往需要使用两个系统调用(select , recvfrom)而BIO只需要调用recvfrom这一个系统调用,如果连接数并不是很多的话,可能延迟还会更大,如果一个连接占用的时间过长也不好,比如要读写的数据很大,就会造成后续已完成数据准备的请求无法及时读写,所以IO多路复用的优势在于处理多连接和短连接
  • IO多路复用的最大优势就是复用线程,开销小,降低了维护难度

I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

select

select函数会监视文件描述符,调用select函数会阻塞,直到有描述符读就绪或者写就绪,或者超过指定的超时时间,函数返回,当select函数返回后,可以通过遍历fdset来找到准备就绪的描述符

select函数的缺点:

  • 单个进程能监视的最大文件描述符的数量有上限,linux上是1024,select函数使用bitmap这个数据结构来对监视的文件描述符做标记,这个bitmap在linux中默认是1024,select函数在运行过程中将这个bitmap拷贝到内核,让内核来判断哪个文件描述符准备就绪,所以,这个数据拷贝过程也会带来的一定的系统开销
  • 当select函数返回后,应用程序还需要以O(N)的复杂度去遍历FD集合,来找到准备就绪的FD,然后进行数据处理
poll

poll本质上和select没有区别,与select的区别的是他没有最大连接数的限制,原因是他是基于链表来存储的

poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。

缺点:

从上面看,select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket。事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。

epoll

epoll是select和poll的增强版本

epoll支持水平触发和边缘触发:

  • 边缘触发:epoll只告诉进程哪些FD准备好了,进程可以不用遍历整个FD集合,降低了开销
  • 水平触发:如果报告了fd后,没有被处理,那么下次epoll时会再次报告该fd。

epoll还支持使用事件的就绪通知方式,通过epoll_ctl注册文件描述符,一旦FD准备就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知

总结
  • BIO:数据准备和数据拷贝都是阻塞的
  • NIO:轮询数据是否准备完成,数据拷贝的过程是阻塞的
  • IO多路复用:两次系统调用,select,poll,epoll监视内核数据准备,数据拷贝的过程仍然是阻塞的,适合于多连接,短连接
  • AIO:数据准备和数据拷贝过程是非阻塞的

输入图片说明

redis IO模型

redis的高性能的一个原因是redis采用网络IO多路复用的技术保证在多连接的时候,系统的高吞吐量

多路-指的是多个socket连接,复用-指的是复用一个线程。多路复用主要有三种技术:select,poll,epoll。epoll是最新的也是目前最好的多路复用技术。

这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗),且Redis在内存中操作数据的速度非常快(内存内的操作不会成为这里的性能瓶颈),主要以上两点造就了Redis具有很高的吞吐量。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值