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模型的两个问题:
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)