C++高性能服务器开发总结系列之【Linux网络IO模型——多路I/O转接】

目录

多路I/O转接模型

select

poll

epoll

what

why

how

 


由于select、poll模型比较久远且应用场景较少,只简要介绍其优缺点,对epoll进行详细的分析。

多路I/O转接模型

多路I/O转接(复用),顾名思义,将I/O请求转接给内核管理,借助内核来替服务器监视有无客户端的连接请求,当有客户端的连接请求时,再经linux下select、poll、epoll的多路I/O复用接口转接给服务器端处理,可以提高服务器的性能。

select

等待文件描述符上发生的事件,通过内核帮助监听,返回就绪文件描述符个数。

优点:

  • 跨平台性:可跨平台

缺点:

  • 最大并发数:linux下限制最大并发数1024(历史原因,早先服务器并没有考虑高并发,文件描述符默认打开最大个数1024,可修改,但增加链接数,效率不高)
  • 底层结构和模型:存储文件描述符底层数据结构是位图,采用轮询模型,效率低,超过1024更加降低效率
  • 状态切换:用户态、内核态之间的频繁拷贝(调用select将数组整个传入拷贝一次,就绪事件返回又将数组整个拷贝一次)
  • 可读性:每次完成监听需要再次重新传入并且分事件传入,操作冗余。

poll

优点:

  • 最大并发数:在资源足够下,描述符无上限(采用链表存放监听的文件描述符)
  • 可读性:监听和返回分离

缺点:

  • 底层结构和模型:与select相同
  • 状态切换:用户态、内核态之间的频繁拷贝
  • 跨平台性:不可跨平台

epoll

what

epoll是Linux下多路复用IO模型接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。

why

优点

  • 最大并发数:在资源足够下,描述符无上限
  • 底层结构和模型:采用事件回调机制,就绪事件直接交付,直接遍历,不用多余的遍历性能,不会随着描述符的增加而下降
  • 状态切换:每个事件只需添加到内核态一次,不需要重复添加,效率高,只在合适的时候调用EPOLL_CTL_ADD将文件描述符结构拷贝到内核中,这个操作并不频繁(而select/poll都是每次循环都要进行拷贝)
  • 可读性:拆分成了三个函数,使用起来更方便高效,不需要每次循环都设置关注的文件描述符,也做到了输入输出参数分离

缺点

  • 跨平台性:不可跨平台
  • 适用性:对于连接较少和大量传输数据的场景,其它模型更加实用。

how

三板斧:epoll_create、epoll_ctl、epoll_wait

int epoll_create(int size)
作用:在内核创建一棵监听红黑树,作为事件表
参数:
size:
    创建的红黑树的监听节点数量。(仅供内核参考)
返回值:成功返回指向新创建的红黑树的根节点的fd,失败返回-1,并设置errno 
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
作用:操作监听红黑树,在红黑树上添加、修改、删除某个epoll监控的文件描述符上的事件
参数:
epfd:
    epoll_create的返回值,即红黑树根节点对应的fd
op:
    对该监听红黑树所做的操作,用3个宏来表示
     EPOLL_CTL_ADD 注册新的fd到监听红黑树
     EPOLL_CTL_MOD 修改已经注册的fd的监听事件
     EPOLL_CTL_DEL 将fd从监听红黑树上摘下,取消监听
fd:
    要监听的fd
event:
    本质是一个结构体,主要描述监听的事件等信息
返回值:
    成功返回0,失败返回-1,并设置errno  
typedef union epoll_data {
    void        *ptr;
    int          fd;          // 对应监听事件的fd
    uint32_t     u32;
    uint64_t     u64;
} epoll_data_t;
 
struct epoll_event {
    uint32_t     events;      // 监听的事件
    epoll_data_t data;        // 用户变量,一般存fd
};
events:表示监听的事件 通常取值如下
    EPOLLIN  表示对应的文件描述符可以读(包括对端SOCKET正常关闭)
    EPOLLOUT 表示对应的文件描述符可以写
    EPOLLERR 表示对应的文件描述符发生错误
    EPOLLPRI 表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来)
    EPOLLHUP 表示对应的文件描述符被挂断
    EPOLLET  将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的
    EPOLLONESHOT 只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

 

epoll_event底层数据结构:

红黑树——监控队列,双向链表——就绪队列
事件回调机制:事件触发,回调函数知道是什么事件,红黑树搜索找到事件,插入双向链表;用户自定义数组,从链表中拷贝,对事件对应的文件描述符进行操作。

优点:避免使用遍历,而是使用回调函数的方式,将就绪的文件描述符结构加入到就绪队列中,epoll_wait返回直接访问就绪队列就知道哪些文件描述符就绪,这个操作是O(1),即使文件描述符数据很多,效率也不会受到影响。

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
作用:监听多个文件描述符的状态变化
参数:
epfd:
    epoll_create的返回值,即红黑树根节点对应的fd
events:
    传出参数,是一个数组,表示满足监听条件的fd对应的epoll_event结构体  
maxevents:
    上述events数组的元素的总个数
timeout:
    -1:阻塞监听
     0:不阻塞监听
    >0:超时时间(毫秒)
返回值:
    >0:满足监听条件的事件的总个数,可用作循环上限
     0:没有fd满足监听条件
    -1:失败,并设置errno

添加事件和描述符时,注册回调函数。当描述符上有事件就绪时,把描述符和事件添加到队列中。
  • 执行epoll_create()时,创建红黑树和就绪链表;

  • 执行epoll_ctl()时,如果增加socket句柄,则检查在红黑树中是否存在,存在立即返回,不存在则添加到树干上,然后向内核注册回调函数,用于当就绪事件来临时向准备就绪链表中插入数据;

  • 执行epoll_wait()时立刻返回准备就绪链表里的数据即可。


工作方式ET/LT:

LT水平触发

当socket上有数据时,用户去处理相应的数据,只要没有处理完成,Epoll会不停地通知有socket就绪(不停触发回调函数添加到就绪队列)

ET边缘触发

当socket上有数据时,用户去处理相应的数据,需要用户自己将数据读取完毕,不管有没有处理完成,Epoll只通知一次有socket就绪(只触发一次回调函数)

使用ET模式更加高效,避免有就绪事件的文件描述符反复添加到就绪队列,重复处理,但在用户层面需要自己处理读写


Reactor模型:

又称反应堆模型,是一种事件驱动处理模式,用于处理一个或多个输入同时交付服务处理的服务请求

服务处理程序对输入的请求,进行多路分解,并将它们分发到关联的请求处理程序

它分为三个模块:reactor、handler、acceptor

reactor:负责将IO事件分发给handler处理

handler:负责处理IO

acceptor:负责处理客户端连接就绪事件,同时accept获取客户端对应的socket

一个单reactor多线程模型的示意图:

为什么要使用Reactor模型呢?

设想这样一种场景,你开了一家饭店,招了一些服务员,当有客人来吃饭时,每一个客人你都安排一个服务员给它点菜上菜,这样虽然每个客人都感受到了最贴心的vip服务,用户体验很好

但是当客人数量特别多时,你总不能雇佣特别多的服务员去给每个客人精致的服务吧?

所以我们开店都会设置一个前台,只雇佣一部分的服务员,客人需要点菜,需要经过前台或者专门的点菜员

服务员做的就是菜做好了上菜的工作,当客人特别多时,老板可以增加雇佣的服务员数量

这个过程就是一个Reactor模型的典型应用,Acceptor就是前台或者点菜员,Handler中的每一个线程就是服务员,这种方式可以对服务器的资源利用更加合理。

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值