redis 网络IO

开门见山,redis直接使用IO多路复用模型中的epoll模型实现和内核的事件传递。

这个过程具体如何实现的,慢慢分析

epoll 源码 linux
https://editor.csdn.net/md/?articleId=121450854

initServer阶段

I/O多路复用 epoll

1. 创建epoll模型实例

创建aeEventLoop server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR)
	 aeApiCreate(eventLoop)
		state->epfd = epoll_create(1024) 创建epoll实例
epoll_create

2. 监听端口 listenToPort

3. 添加文件事件 epoll_ctl 将fd 参数传递到内核

if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE, acceptTcpHandler,NULL) == AE_ERR)
	if (aeApiAddEvent(eventLoop, fd, mask) == -1) return AE_ERR 
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
        aeFileProc *proc, void *clientData)
{

参数:
aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE, acceptTcpHandler,NULL)

  • server.el
  • server.ipfd[j]
  • AE_READABLE 这里是一个可读事件,所以当可读事件到来,会通过acceptTcpHandler连接处理应答器进行处理
  • acceptTcpHandler

在这里插入图片描述

aeFileProc 事件处理函数

aeFileProc函数指针定义如下:

typedef void aeFileProc(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask);
在c语言中,回调是通过函数指针实现的。 通过将回调函数地址 传递给 被调函数,从而实现回调。在这里,通过定义函数指针aeFileProc,由调用方实现具体的函数内容,在实际调用函数里,把aeFileProc实现函数的地址传进来。其实相当于定义一种接口,由调用方来实现该接口

aeApiAddEvent

在这里插入图片描述

static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
    aeApiState *state = eventLoop->apidata;
    struct epoll_event ee = {0}; /* avoid valgrind warning */
    /* If the fd was already monitored for some event, we need a MOD
     * operation. Otherwise we need an ADD operation. */
     * 这里的op表示是添加event还是修改已经存在的event,因为每次通过epoll_ctl 不仅要向内核传递,是一个动作,epollctl支持的动作就三个
     * EPOLL_CTL_ADD:表示要进行添加操作。
     * EPOLL_CTL_DEL:表示要进行删除操作。
     * EPOLL_CTL_MOD:表示要进行修改操作。
    int op = eventLoop->events[fd].mask == AE_NONE ?
            EPOLL_CTL_ADD : EPOLL_CTL_MOD;

    ee.events = 0;
    mask |= eventLoop->events[fd].mask; /* Merge old events */
    
    //监控读和写事件,如果mask为可读,设置 ee.events |= EPOLLIN; 0和任何二进制进行或运算 都是二进制位本身相当于ee.events = EPOLLIN
    if (mask & AE_READABLE) ee.events |= EPOLLIN;
    if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;
    //#要监控的fd
    ee.data.fd = fd;
    //将前面设置好的epfd/op/fd/ee 通过epoll_ctl 设置到kernel space
    if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;
    return 0;
}

aeEventLoop开始工作 epoll_wait

aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP)
	 numevents = aeApiPoll(eventLoop, tvp);

aeApiPoll

在这里插入图片描述

3:最后通过epoll_wait 来等待event时间的发生。
static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
    aeApiState *state = eventLoop->apidata;
    int retval, numevents = 0;
    #通过epoll_wait 等待epfd事件的发生,这里有设置timeout的时间,在规定的时间内epoll_wait 必须返回
    retval = epoll_wait(state->epfd,state->events,eventLoop->setsize,
            tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);
    #retval大于0说明是有event发生了,如果是timeout返回则retval等于-1
    if (retval > 0) {
        int j;
 
        numevents = retval;
        #遍历state->events 数据分别找出是读还是写event
        for (j = 0; j < numevents; j++) {
            int mask = 0;
            struct epoll_event *e = state->events+j;
	    #根据mask 判断是读还是写事件
            if (e->events & EPOLLIN) mask |= AE_READABLE;
            if (e->events & EPOLLOUT) mask |= AE_WRITABLE;
            if (e->events & EPOLLERR) mask |= AE_WRITABLE;
            if (e->events & EPOLLHUP) mask |= AE_WRITABLE;
	    #保持发生event的fd和保存fd对于的mask
            eventLoop->fired[j].fd = e->data.fd;
            eventLoop->fired[j].mask = mask;
        }
    }
    return numevents;
}
————————————————
版权声明:本文为CSDN博主「tiantao2012」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/tiantao2012/article/details/79527715
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值