对于redis框架的理解(四)

上一篇讲述了eventloop的结构和创建,添加文件事件删除文件事件,派发等等。

而eventloop主要就是调用不同网络模型完成事件监听和派发的。

这一篇主要讲述epoll网络模型,redis是如何封装和调用的

 下面是epoll_event的结构

/*
    epoll_event 结构

    struct epoll_event
    {
        uint32_t events; //epoll_event 要注册的事件类型
        epoll_data_t data; //User data  //联合体用于存储用户要保存的数据
    }

    typedef union epoll_data
    {
        void * ptr;
        uint32_t u32;
        uint64_t u64;
        int fd;  //一般存储accept后生成的socketfd
        
    }epoll_data_t 

*/

 

Ae_epoll.c文件中回传的数据结构

#include <sys/epoll.h>
//该结构用于回传eventLoop->apidata
typedef struct aeApiState {
    int epfd; //管理epoll事件表的句柄
    struct epoll_event *events; //epoll events的队列
} aeApiState;

 

Ae_epoll.c中创建epoll句柄

//epoll 创建epfd过程
static int aeApiCreate(aeEventLoop *eventLoop) {

    //开辟存储不同网络模型的数据块
    aeApiState *state = zmalloc(sizeof(aeApiState));

    if (!state) return -1;
    //开辟epoll_event * size 大小的空间,这段空间是连续的
    state->events = zmalloc(sizeof(struct epoll_event)*eventLoop->setsize);
    //开辟失败
    if (!state->events) {
        zfree(state);
        return -1;
    }
    //创建epfd,最多关注1024个文件描述符
    state->epfd = epoll_create(1024); /* 1024 is just a hint for the kernel */
    if (state->epfd == -1) {
        zfree(state->events);
        zfree(state);
        return -1;
    }
    
    // eventLoop->apidata数据回传
    eventLoop->apidata = state;
    return 0;
}

 

 

Ae_epoll.c重新设置events队列大小

//重新设置aeApiState大小
static int aeApiResize(aeEventLoop *eventLoop, int setsize) {
    aeApiState *state = eventLoop->apidata;

    state->events = zrealloc(state->events, sizeof(struct epoll_event)*setsize);
    return 0;
}

 

Ae_epoll.c中释放内存和回收

//释放aeApiState和 events 的内存
static void aeApiFree(aeEventLoop *eventLoop) {
    aeApiState *state = eventLoop->apidata;
    //关闭文件描述符
    close(state->epfd);
    //释放events的内存
    zfree(state->events);
    //释放aeApiState 的内存
    zfree(state);
}

 

Ae_epoll.c添加读写事件或者更改读写事件的函数

//epoll 注册事件,读或者写
static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
    //aeEventLopp 的数据域
    aeApiState *state = eventLoop->apidata;
    //epoll_event 事件
    struct epoll_event ee;
   
     //aeEventLoop 中注册的文件事件队列标志位如果不是AE_NONE,那么更改,否则添加
    int op = eventLoop->events[fd].mask == AE_NONE ?
            EPOLL_CTL_ADD : EPOLL_CTL_MOD;

    //events读写事件清零
    ee.events = 0;
    //aeEventLoop 中注册的文件事件标志位进行融合
    mask |= eventLoop->events[fd].mask; /* Merge old events */
    //如果是读事件,那么将epoll_event 注册读事件
    if (mask & AE_READABLE) ee.events |= EPOLLIN;
    //如果是写事件,那么将epoll_event 注册写事件
    if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;
    ee.data.u64 = 0; /* avoid valgrind warning */
    //epoll_event 文件描述符
    ee.data.fd = fd;
    //将epoll事件注册到epoll的事件表里
    if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;
    return 0;
}

 

Ae_epoll.c中删除读写事件的函数

 

static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int delmask) {
    aeApiState *state = eventLoop->apidata;
    struct epoll_event ee;
    //这是创建的逆过程
    //按位去反,按位&,即去掉相应的标志位
    int mask = eventLoop->events[fd].mask & (~delmask);

    ee.events = 0;
    //判断此时文件事件是读
    if (mask & AE_READABLE) ee.events |= EPOLLIN;
    //判断此时文件事件是写
    if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;
    
    ee.data.u64 = 0; /* avoid valgrind warning */
    ee.data.fd = fd;
    
    if (mask != AE_NONE) {
        //更改epoll_event的事件类型
        epoll_ctl(state->epfd,EPOLL_CTL_MOD,fd,&ee);
    } else {
        /* Note, Kernel < 2.6.9 requires a non null event pointer even for
         * EPOLL_CTL_DEL. */
         //删除epoll_event 事件
        epoll_ctl(state->epfd,EPOLL_CTL_DEL,fd,&ee);
    }
}

 

事件派发函数

//epoll 事件派发
static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
    aeApiState *state = eventLoop->apidata;
    int retval, numevents = 0;

    //epoll wait 返回就绪状态的文件描述符,后面的结构体如果为空,那么说明阻塞,不为空表示等待多少秒后返回
    //下面是man手册的解释
    //Specifying a timeout of -1 makesepoll_wait(2) wait indefinitely, while specifying 
    //a timeout equal to zero makesepoll_wait(2) to return immediately 
    //even if no events are available (return code equal to zero)
    retval = epoll_wait(state->epfd,state->events,eventLoop->setsize,
            tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);
    if (retval > 0) {
        int j;

        numevents = retval;
        //轮询处理已经就绪的文件描述符
        for (j = 0; j < numevents; j++) {
            int mask = 0;
            //指针+j,表示每次便宜地址为j*epoll_event个字节
            struct epoll_event *e = state->events+j;

            //可读事件
            if (e->events & EPOLLIN) mask |= AE_READABLE;
            //可写事件
            if (e->events & EPOLLOUT) mask |= AE_WRITABLE;
            //处理错误发送给客户端
            if (e->events & EPOLLERR) mask |= AE_WRITABLE;
            //对端正常关闭(程序里close(),shell下kill或ctr+c),
            //触发EPOLLIN和EPOLLRDHUP,但是不触发EPOLLERR和EPOLLHUP。
            if (e->events & EPOLLHUP) mask |= AE_WRITABLE;
            //添加到aeApiState 的就绪事件队列里
            eventLoop->fired[j].fd = e->data.fd;
            //就绪时间状态
            eventLoop->fired[j].mask = mask;
        }
    }
    return numevents;
}
//网络模型名字
static char *aeApiName(void) {
    return "epoll";
}

 

以上是封装的epoll结构和解释

下一篇去解读其他的网络模型

我的微信公众号

 

转载于:https://my.oschina.net/secondtonone1/blog/731017

源码来自:https://pan.quark.cn/s/a4b39357ea24 《C++ Primer》作为C++编程领域中的一部权威著作,主要服务于初学者和经验丰富的开发者,致力于帮助他们深入掌握C++的核心知识。 第一章通常会详细讲解C++语言的基础概念和语法结构,包括变量的使用、数据类型的分类、常量的定义、运算符的应用以及基础的输入输出操作。 接下来,我们将对这一章中的核心知识点和可能的习题解答进行深入分析。 ### 1. 变量与数据类型在C++编程中,变量被视为存储数据的媒介。 每一个变量都必须预先声明其数据类型,常见的数据类型有整型(int)、浮点型(float)、双精度浮点型(double)以及字符型(char)。 例如:```cppint age = 25; // 声明一个整型变量age并赋予其初始值25float weight = 70.5f; // 声明一个浮点型变量weight并赋予其初始值70.5char grade = A; // 声明一个字符型变量grade并赋予其初始值A```### 2. 常量与字面量常量指的是不可更改的值,可以通过`const`关键字进行声明。 例如:```cppconst int MAX_SIZE = 100; // 声明一个整型常量MAX_SIZE,其值为100```字面量是指程序中直接书写的值,如`42`、`3.14`或`"Hello"`。 ### 3. 运算符C++提供了多种运算符,涵盖了算术运算符(+,-,*,/,%)、比较运算符(==,!=,<,>,<=,>=)、逻辑运算符(&&,||,!)以及赋值运算符(=,+=,-=,*=,/=,%=)等。 ### 4. 输入与输出在C++中,使用`std::cin`来实现输...
内容概要:本文详细介绍了一个基于C++的仓库存储管理系统的设计与实现,涵盖了项目背景、目标、挑战及解决方案,并系统阐述了整体架构设计、数据库建模、功能模块划分、权限安全、并发控制、数据一致性保障、异常处理与可扩展性等关键内容。通过面向对象编程思想,采用分层架构与模块化解耦设计,结合STL容器、多线程、锁机制等C++核心技术,实现了高效的库存管理功能,包括入库、出库、盘点、调拨、权限控制、日志追踪与智能报表分析。文中还提供了核心类如Inventory(库存)、User(用户权限)、LogEntry(操作日志)及WarehouseManager(主控制器)的代码示例,展示了数据结构设计与关键算法逻辑。; 适合人群:具备C++编程基础,熟悉面向对象设计与基本数据结构的软件开发人员,尤其适合从事企业级管理系统开发或希望深入理解系统架构设计的中级开发者(工作1-3年);也适用于计算机相关专业学生进行课程设计或毕业项目参考; 使用场景及目标:①学习如何使用C++构建复杂业务系统的整体架构与模块划分方法;②掌握高并发、数据一致性、权限控制、异常处理等企业级系统关键技术的实现思路;③理解仓储管理业务流程及其在软件系统中的建模与落地方式;④为开发类似ERP、MES等后台管理系统提供技术原型与设计参考; 阅读建议:此资源不仅提供理论架构与代码片段,更强调系统设计的完整性与工程实践性。建议读者结合代码示例动手实现核心模块,深入理解类之间的关系与交互逻辑,重点关注多线程安全、事务管理与权限校验等难点环节,并尝试扩展功能如对接GUI界面或数据库持久化模块,以全面提升系统开发能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值