浅谈IO多路复用技术

浅谈IO多路复用技术,第1张

I/O多路复用,即一个线程可以处理很多个socket连接。一个select/poll/epoll系统调用可以处理很多个socket连接,而不像recv等系统调用每次只能处理一个sock。

服务端主线程只负责accept新的连接,每次有新连接则抛出一个线程处理该连接的数据接收与发送。

缺点:开销大,性能差

服务端将监听与接收都设置为非阻塞,轮询所有已建立连接的socket。

缺点:大量的CPU时间浪费在无效的轮询上面(对每个有效/无效的sock连接都调用recv等系统调用,而每次系统调用都会造成上CPU下文切换)

一个select/poll/epoll系统大中乎调用可以处理很多个socket连接,每次返回告诉服务端哪些sock可以读/写,从而服务器只对有效的sock进行读写。

I/O多路复用器时同步I/O模型:它只向用户程序返回状态(可读写),程序自己得去accept/recv/write。

异步I/O模型指的是有一个方法,你调用了,返回时除了返回可读写状态,同时数据也帮你写入用户程序了,不需要自己去recv。

1)fd重复传递(相同的fd重复从用户态传到内核态); 解决方案:在滚悉内核开辟一个空间记录已传递的fd

2)被动遍历(内核没有记录已传递的fd,每次select调用都得遍历所有的fd); 解决方案:内核存储已传递的fd,每次网卡驱动有事件知道是哪个fd,下次epoll调用直接知道哪些fd有效。

1) epoll_creat()在内核开辟空间记录已传递的fds。 函数的返回值epoll_fd即代表内核开辟的空间(以红黑树存储)。

2) epoll_ctl(epoll_fd, ADD, listen_fd, ACCEPT/RECV/WRITE)负责往epoll_fd指向的内核空间中添加新的socket连接及该socket连接上需要监听的事件类型。 即每个socket描述符只传递一次。

3) epoll_wait() 返回有响应的fd集合(rd_list等待区),返回服务器程序处理。epoll通过网卡的异步消培并息机制,将红黑数里边有事件响应的fd放入rd_list。

用过redis的人,或者去面试的人被问redis的问题,基本都会了缺型拿解redis特别快。

redis高校的原因:

说起IO多路复用,可以先从IO模型开始了解。

BIO是一种同步阻塞的IO模型,由用户程序线程发起请求,完成数据的复制接收。

NIO是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,成为解决高并发与大量连接、I/O处理问题的有效方式。

IO多路复用模型,就是通过一种新的系统调用,一个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是内核缓冲区可读/可写),内核kernel能够通知程序进行相应的IO系统调用。

目前支持IO多路复用的系统调用,有 select,epoll等等。select系统调用,是目前几乎在所有的 *** 作系统上都有支持,具有良好跨平台特性。epoll是在linux 2.6内核中提出的,是select系统调用的linux增强版本。

在内核kernel的等待数据和复制数据的两个阶段,用户线程都不是block(阻塞)的。用户线程需要接受kernel的IO *** 作完成的事件,或者说注册IO *** 作完成的回调函数,到 *** 作系统的内核。所以说,异步IO有的时候,也叫做信号驱动 IO 。

在以上多种IO模型中,各个模型有自己的特点,也有各自的优缺点。

优点:

缺点:

综上,在简单的低并发的场景下,为了降低程序的复杂度,可以使用BIO,在高并发的场景下,不建议使用。

优点:

缺点:

综上,可以看出,虽然具有非阻塞的特点,但是在高并发的场景下,也存在着巨大的消耗问题。单纯的使用NIO,并不建议使用。

优点:

缺点:

综上,虽然从模型上看,异步IO模型在各方面综合比较来说,的确有非常大的优势,但是还在完善阶段,未来肯定会有更好的发挥作用,但是在现在的场景下,使用的还是相对较少。

优点:

缺点:

综上所述,IO多路复用其实也是有阻塞的,只不过相对于一个线程维护一个连接,大大提高了性能,减少了系统的开销。

IO多路复用也是大多数框架使用的IO模型。

redis服务器中有两类事件:

文件事件是对套接字 *** 作的抽象,每当一个套接字准备好执行连接应答、写入、读取、关闭等 *** 作时,就会产生一个文件事件。

包括可读事件、可租迟写事件。

针对套接字、文件事件、文件描述符等概念后续计划出伏搭一篇文章进行讲解。

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

epoll跟select都能提供多路I/O复用的解决方宽者迹案。在现在的Linux内核里有都能够支持,其中epoll是Linux所特有,而select则应该是POSIX所规定,一般 *** 作系统均有实现

Select模型使用单个线程监听客户端连接请求,嫌碧连接建立成功后注册读写事件到Selector中,Selector调用 *** 作系统内核的方法注册读写事件,然后Selector阻塞等待读写事件。

客户端发送数据到服务端, *** 作系统内核轮训socket连接的读写事件,当存在读写事件时,生成对应的可读列表readList和可写列表writeList,应用层遍历读写事件列表readList和writeList,做相应的读写 *** 作,进行后续的业务处理。

从上面的流程可以看出,主要存在两个问题:

(1) 轮训,每次调用时都会对连接进行线性遍历,所以随着连接数的增加会造成遍历速度慢的“线性下降性能问题”,性能瓶颈可能会出现。

(2) 连接数量。连接数量首先影响轮训效率,其次,如果连接数很多,连接在应用层和内核层的相互copy,也会有一定的性能影响。

epoll模型主要解决了select模型的两个问题:


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/yw/12545242.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-26
下一篇 2023-05-26

发表评论

登录后才能评论

评论列表(0条)

保存