4.1 网络IO模型以及Redis的实现

网络IO模型通过内核的不断升级,现在已经有5种模式,这5种模式各有利弊。

五种IO模型

阻塞IO

监听一个fd,传递给内核,然后阻塞等待,内核发现此fd有事件产生时返回fd以及事件

非阻塞IO

监听一个fd,传递给内核,然后不管fd是否有事件发生,直接返回处理其他事件,但会一直轮询问内核是否fd有事件发生.这样用户空间一直对系统调用,造成cpu资源浪费,减低效率

异步IO

当前只有windows的iocp属于异步io。异步io必须满足2个条件:

  1. 内核到用户空间是无阻塞的。
  2. 硬件到内核是无阻塞的
    而其他IO模型内核空间拷贝数据到用户态是阻塞的,只有iocp是系统内核执行完成后才告知用户。
信号驱动IO

监听一个fd,传递给内核,非阻塞,后续内核收到此fd事件变化,发送一个信号给用户。

多路复用IO

前面的这些IO模型都是监听一个fd,如果系统有100万个连接,那么则需要去实现100万个线程去监听这些IO,这样对系统来说肯定是并发很低的。我们为了解决这个问题,产生了多路复用IO,也就是说,一次性监听多个fd,然后内核发现fd这些fd的某些有事件触发,则返回。对于这种模型linux有3种实现方式

  • select
// readfds:关心读的fd集合;writefds:关心写的fd集合;excepttfds:异常的fd集合  
int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);  

传递3个不同事件监听类型fd数组下去,然后内核对这些数据进行for循环。如果对应数组有对应事件产生,则返回通知,如果timeout内没有任何事件,会告诉超时。时间复杂度比较高,是O(n)。
select基本所有平台都支持,但是select缺点除了时间复杂度以外,还有只支持最大连接数1024,如果要修改这个连接数,需要修改常量后重新编译内核。

  • poll
int poll (struct pollfd *fds, unsigned int nfds, int timeout);  
  
struct pollfd {  
    int fd; /* file descriptor */  
    short events; /* requested events to watch */  
    short revents; /* returned events witnessed */  
};  

poll传递的参数是一个fd以及events在一起的结构体数组。在内核也是对这个数据进行for循环,遍历查看是否有对应事件产生。但是这个是没有最大连接数的限制的。

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

  • epoll
//创建epollFd,底层是在内核态分配一段区域,底层数据结构红黑树+双向链表  
int epoll_create(int size);//创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大  
  
//往红黑树中增加、删除、更新管理的socket fd  
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);  
  
//这个api是用来在第一阶段阻塞,等待就绪的fd。  
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);  

epoll对fd的监听模式有2种可以选择,LT以及ET

  • LT 水平触发
    无论fd是否发生变化,每次都会告知用户态当前fd的状态如何
  • ET 边缘触发
    只有fd发生变化,才会告知用户态,告知之后,下一次不会再告诉当前fd的状态信息

Reactor模型介绍

Reactor模型指在使用多路复用IO时,对于用户态fd的事件管理模型

Reactor单线程模型

业务逻辑处理和fd的读写IO都在同一个线程实现

Reactor多线程模型

业务逻辑处理和fd的读写IO不在同一个线程实现

multi-reactor 多线程模型

业务处理多线程以及读写IO的事件分发机制也采用多线程处理

Redis的网络IO模型

Redis采用单线程为何支持高并发
  • Redis使用的内存IO,不是磁盘IO,大大降低了IO时间
  • Redis单线程,无需去考虑多线程造成的死锁问题
  • Redis单线程,底层网络IO模型使用多路复用epoll方式(如果内核不支持epoll,可自动切换到select或者poll,看配置信息可进行修改)
Redis6实现了多线程,作用是什么

Redis6实现的多线程,只是对网络IO读写处理做多线程处理,但是对命令行的操作仍然是单线程的。这样即加快了IO处理效率,又保证了原子性。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容