libevent学习之跨平台Reactor接口的实现

原文链接:http://www.jb51.cc/article/p-kofkkjra-gp.html

上文学习了Libevent中的TAILQ_QUEUE,Libevent最重要的跨平台功能还是实现了多路IO接口的跨平台(即Reactor模式)。这使得用户可以在不同的平台使用统一的接口。这篇博文就是来讲解Libevent是怎么实现这一点的。对于自己以后编写跨平台的网络程序很有好处.

event_base结构体结构体->struct eventop *evsel

struct eventop {  
    const char *name; //多路IO复用函数的名字 

    void *(*init)(struct event_base *);  

    int (*add)(struct event_base *,evutil_socket_t fd,short old,short events,void *fdinfo);  
    int (*del)(struct event_base *,void *fdinfo);  
    int (*dispatch)(struct event_base *,struct timeval *);  
    void (*dealloc)(struct event_base *);  

    int need_reinit; //是否要重新初始化 
    //多路IO复用的特征。参考http://blog.csdn.net/luotuo44/article/details/38443569 
    enum event_method_feature features;  
    size_t fdinfo_len; //额外信息的长度。有些多路IO复用函数需要额外的信息 
};

struct eventop结构体的成员就是一些函数指针。名称也像一个多路IO复用函数应该有的操作:add可以添加fd,del可以删除一个fd,dispatch可以进入监听。明显只要给event_base的evsel成员赋值就能使用对应的多路IO复用函数了。

Libevent源代码中为每种多路IO复用函数创建了一个文件,比如select.c、poll.c、epoll.c、kqueue.c等。

打开这些文件就可以发现在文件的前面都会声明一些多路IO复用的操作函数,而且还会定义一个struct eventop类型的全局变量。

//select.c文件 
static void *select_init(struct event_base *);  
static int select_add(struct event_base *,int,void*);  
static int select_del(struct event_base *,void*);  
static int select_dispatch(struct event_base *,struct timeval *);  
static void select_dealloc(struct event_base *);  

const struct eventop selectops = {  
    "select",select_init,select_add,select_del,select_dispatch,select_dealloc,0,/* doesn't need reinit. */  
    EV_FEATURE_FDS,};

如何选定后端:
看到这里,读者想必已经知道,只需要将对应平台的多路IO复用函数的全局变量赋值给event_base的evsel变量即可.可是系统支持多种IO复用,libevent是如何根据系统环境选择一个IO复用类型的呢?

/* Array of backends in order of preference. */
static const struct eventop *eventops[] = {
#ifdef _EVENT_HAVE_EVENT_PORTS
    &evportops,#endif
#ifdef _EVENT_HAVE_WORKING_KQUEUE
    &kqops,#endif
#ifdef _EVENT_HAVE_EPOLL
    &epollops,#endif
#ifdef _EVENT_HAVE_DEVPOLL
    &devpollops,#endif
#ifdef _EVENT_HAVE_POLL
    &pollops,#endif
#ifdef _EVENT_HAVE_SELECT
    &selectops,#endif
#ifdef WIN32
    &win32ops,#endif
    NULL
};

它根据宏定义判断当前的OS环境是否有某个多路IO复用函数。如果有,那么就把与之对应的struct eventop结构体指针放到一个全局数组中。有了这个数组,现在只需将数组的某个元素赋值给evsel变量即可。

从数组的元素可以看到,低下标存的是高效多路IO复用函数。如果从低到高下标选取一个多路IO复用函数,那么将优先选择高效的。

选择一个可用的IO复用函数类型是在event_base_new_with_config(const struct event_config *cfg)函数中

for (i = 0; eventops[i] && !base->evbase; i++) {
        if (cfg != NULL) {
            /* determine if this backend should be avoided */
            if (event_config_is_avoided_method(cfg,eventops[i]->name))
                continue;
            if ((eventops[i]->features & cfg->require_features)
                != cfg->require_features)
                continue;
        }

        /* also obey the environment variables */
        if (should_check_environment &&
            event_is_method_disabled(eventops[i]->name))
            continue;

        //选择其中一个系统可用的IO复用模型
        base->evsel = eventops[i];

        //初始化evbase,后面会说到,init主要是做什么事情呢?
        base->evbase = base->evsel->init(base);
    }

后端数据存储结构体
在本文最前面列出的event_base结构体中,除了evsel变量外,还有一个evbase变量。这也是一个很重要的变量,而且也是用于跨平台的。
像select、poll、epoll之类多路IO复用函数在调用时要传入一些数据,比如监听的文件描述符fd,监听的事件有哪些。在Libevent中,这些数据都不是保存在event_base这个结构体中的,而是存放在evbase这个指针指向的一个结构体中。

IO复用结构体:
由于不同的多路IO复用函数需要使用不同格式的数据,所以Libevent为每一个多路IO复用函数都定义了专门的结构体(即结构体是不同的),本文姑且称之为IO复用结构体。evbase指向的就是这些结构体。由于这些结构体是不同的,所以要用一个void类型指针。
在select.c、poll.c这类文件中都定义了属于自己的IO复用结构体,如下面代码所示:

//select.c文件 
struct selectop {  
    int event_fds;      /* Highest fd in fd set */  
    int event_fdsz;  
    int resize_out_sets;  
    fd_set *event_readset_in;  
    fd_set *event_writeset_in;  
    fd_set *event_readset_out;  
    fd_set *event_writeset_out;  
};  

//poll.c文件 
struct pollop {  
    int event_count;        /* Highest number alloc */  
    int nfds;           /* Highest number used */  
    int realloc_copy;       /* True iff we must realloc * event_set_copy */  
    struct pollfd *event_set;  
    struct pollfd *event_set_copy;  
};

前面event_base_new_with_config的代码中,有下面一行代码:
base->evbase = base->evsel->init(base);
明显这行代码就是用来赋值evbase的。下面是poll对应的init函数:

//poll.c文件 
static void *  
poll_init(struct event_base *base)  
{  
    struct pollop *pollop;  

    if (!(pollop = mm_calloc(1,sizeof(struct pollop))))  
        return (NULL);  

    evsig_init(base);//其他的一些初始化 

    return (pollop);  
}

经过上面的一些处理后,Libevent在特定的OS下能使用到特定的多路IO复用函数。 由于有evsel和evbase这个两个指针变量,当初始化完成之后,再也不用担心具体使用的多路IO复用函数是哪个了。evsel结构体的函数指针提供了统一的接口,上层的代码要使用到多路IO复用函数的一些操作函数时,直接调用evsel结构体提供的函数指针即可。也正是如此,Libevent实现了统一的跨平台Reactor接口。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom