libevent源码分析

文章目录

  • 1 libevent简介
  • 2 工作原理
    • 2.1 工作流程图
    • 2.2 Reactor模式框架
  • 3 源码分析
    • 3.1 event_init()
      • 3.1.1 函数定义
      • 3.1.2 函数说明
      • 3.1.3 函数源码分析
    • 3.2 event_set()
      • 3.2.1 函数定义
      • 3.2.2 函数说明
      • 3.2.3 函数源码分析
    • 3.3 event_base_set()
      • 3.3.1 函数定义
      • 3.3.2 函数说明
      • 3.3.3 函数源码分析
    • 3.4 event_add()
      • 3.4.1 函数定义
      • 3.4.2 函数说明
      • 3.4.3 函数源码分析
    • 3.5 event_base_loop()
      • 3.5.1 函数定义
      • 3.5.2 函数说明
      • 3.5.3 函数源码分析

1 libevent简介

libevent是一个事件通知库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、kqueue、IOCP等系统调用管理事件机制。著名分布式缓存软件memcached也是基于libevent,而且libevent在使用上可以做到跨平台,而且根据libevent官方网站上公布的数据统计,似乎也有着非凡的性能。

2 工作原理

2.1 工作流程图

libevent源码分析_第1张图片

1 首先初始化一个event_base。调用event_init()
2 新生成一个事件event。设置好监听的对象,监听对象的动作,动作产生之后的回调函数,已经回调函数的参数。掉用event_set()完成。
3 设置事件所属的event_base。调用event_base_set()完成。
4 注册这个事件。主要通过event_add()完成。
    a)对于定时事件,libevent使用一个小根堆管理,key为超时时间,timeheap;
     b)对于I/O事件,libevent将其放入到等待链表(wait list)中,这是一个双向链表结构 eventqueue;
    c)对于Signal和,libevent将其放入到sig集合中
5 程序调用event_base_dispatch()系列函数进入无限循环,等待事件,
以epoll()函数为例;
每次循环前libevent会检查定时事件的最小超时时间tv,根据tv设置select()的最大等待时间,以便于后面及时处理超时事件;
当epoll()返回后,首先检查超时事件,然后检查I/O事件;
Libevent将所有的就绪事件,放入到激活链表中;
然后对激活链表中的事件,调用事件的回调函数执行事件处理activequeues;

2.2 Reactor模式框架

libevent源码分析_第2张图片

Reactor模式框架参考博客:
https://www.cnblogs.com/lfsblack/p/5498556.html

源码实现可以看看:https://blog.csdn.net/baidu20008/article/details/41378761

3 源码分析

3.1 event_init()

3.1.1 函数定义

struct event_base *  event_init(void) 

3.1.2 函数说明

首先初始化struct event_base 变量,并保存返回的指针。实际上这一步相当于初始化一个 Reactor 实例;在初始化 libevent 后,就可以注册事件了。

3.1.3 函数源码分析

1 struct event_base。事件基础类,相当于Reactor

struct event_base {
 const struct eventop *evsel; ////表示选择的事件引擎,可能为:epoll, poll, select
 void *evbase; //指向IO复用机制真正存储的数据,它通过evsel成员的init函数来进行初始化
 int event_count;		/* counts number of total events */
 int event_count_active;	/* counts number of active events */

 int event_gotterm;		/* Set to terminate loop */
 int event_break;		/* Set to terminate loop immediately */

 /* active event management */
 struct event_list **activequeues;
 /*是一个二级指针,前面讲过libevent支持事件优先级,因此你可以把它看作是数组,
 *  其中的元素activequeues[priority]是一个链表, 链表的每个节点指向一个优先级为priority的就绪事件event。*/
 int nactivequeues;

 /* signal handling info */
 struct evsignal_info sig;

 struct event_list eventqueue;////链表,保存了所有的注册事件event的指针。
 struct timeval event_tv; //系统的当前时间

 struct min_heap timeheap;////用来检测事件是否超时的堆栈,是管理定时事件的小根堆

 struct timeval tv_cache;//与event::ev_timeout进行比较,确定事件是否超时
};

2 struct eventop,Libevent为了封装IO复用技术,定义了一个统一的事件操作结构体eventop。

//Libevent为了封装IO复用技术,定义了一个统一的事件操作结构体eventop:
//这里可以理解为定了了一个类eventop。 epoll、 poll、 dev/poll、 select 和 kqueue都是这个类的实例
//类的属性是name,need_reinit
//类方法是init,add,del,dispatch,dealloc
struct eventop {
	const char *name; // 后端IO复用技术的名称
	void *(*init)(struct event_base *);
	int (*add)(void *, struct event *);
	int (*del)(void *, struct event *);
	int (*dispatch)(struct event_base *, void *, struct timeval *);
	void (*dealloc)(struct event_base *, void *);
	/* set if we need to reinitialize the event base */
	int need_reinit;
};

/*epoll.c 定义实例epollops,(默认方式)
 *epollops可以理解为是eventop一个实例。
 * 如果是面向对象写的, eventop epollops = new eventop("epoll",epoll_init,epoll_add,epoll_del,epoll_dispatch,epoll_dealloc,)
*/
const struct eventop epollops = {
	"epoll",
	epoll_init,
	epoll_add,
	epoll_del,
	epoll_dispatch,
	epoll_dealloc,
	1 /* need reinit */
};

/*
  poll.c 定义实例pollops
*/
const struct eventop pollops = {
	"poll",
	poll_init,
	poll_add,
	poll_del,
	poll_dispatch,
	poll_dealloc,
    0
};

/*
  select.c 定义实例selectops
*/
const struct eventop selectops = {
	"select",
	select_init,
	select_add,
	select_del,
	select_dispatch,
	select_dealloc,
	0
};

3 event_init 初始化了一个struct event_base变量,并且会选择I/O复用的具体类型。这里Linux下面默认是epoll。

struct event_base *
event_init(void)
{
	struct event_base *base = event_base_new();

	if (base != NULL)
		current_base = base;

	return (base);
}

struct event_base *
event_base_new(void)
{
	int i;
	struct event_base *base;

	
	
    //设置base->timeheap = -1
	min_heap_ctor(&base->timeheap);
	TAILQ_INIT(&base->eventqueue);

    //设置信号量
	base->sig.ev_signal_pair[0] = -1;
	base->sig.ev_signal_pair[1] = -1;
	
	base->evbase = NULL;

    //选择事件驱动类型;eventops 数组第一个元素是epoll
	for (i = 0; eventops[i] && !base->evbase; i++) {
		base->evsel = eventops[i]; //&epollops
        
        /* 指向IO复用机制真正存储的数据,它通过evsel成员的init函数来进行初始化 */
        /* 比如epoll时,evbase指向的是epollop 这个地方很重要*/
		base->evbase = base->evsel->init(base);
	}


	/* allocate a single active event queue */
	event_base_priority_init(base, 1);

	return (base);
}

4 base->evbase = base->evsel->init(base);在epoll方式中实际上是调用的epoll_init。

struct evepoll {
	struct event *evread; //读回调事件
	struct event *evwrite;//写回调事件
};

typedef union epoll_data
{
      void *ptr;
        int fd;
          uint32_t u32;
            uint64_t u64;
} epoll_data_t;

struct epoll_event
{
      uint32_t events;  /* Epoll events */
        epoll_data_t data;    /* User data variable */
} __attribute__ ((__packed__));


struct epollop {
	struct evepoll *fds; //你关注的socket数组,hash[fd] = evepoll。evepoll是事件struct event
	int nfds;//您关注socket数的大小
	struct epoll_event *events; //epoll_event数组
	int nevents; //epoll_event数组大小默认是32
	int epfd; //epoll_create(32000),你关注的socket以及socket上面发生的事件
};

static void *
epoll_init(struct event_base *base)
{
	int epfd;
	struct epollop *epollop;

	/* Disable epollueue when this environment variable is set */
	if (evutil_getenv("EVENT_NOEPOLL"))
		return (NULL);

	/* Initalize the kernel queue */
    //函数生成一个epoll专用的文件描述符。它其实是在内核申请一空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。size(32000)就是你在这个epoll fd上能关注的最大socket fd数。随你定好了。只要你有空间
	if ((epfd = epoll_create(32000)) == -1) {
		if (errno != ENOSYS)
			event_warn("epoll_create");
		return (NULL);
	}

	FD_CLOSEONEXEC(epfd);

	if (!(epollop = calloc(1, sizeof(struct epollop))))
		return (NULL);

    //epfd 赋值给epollop
	epollop->epfd = epfd;

	/* Initalize fields 初始值:32 个epoll_event*/
	epollop->events = malloc(INITIAL_NEVENTS * sizeof(struct epoll_event));
	if (epollop->events == NULL) {
		free(epollop);
		return (NULL);
	}
	epollop->nevents = INITIAL_NEVENTS;

	//初始值 32 个 evepoll
	epollop->fds = calloc(INITIAL_NFILES, sizeof(struct evepoll));
	if (epollop->fds == NULL) {
		free(epollop->events);
		free(epollop);
		return (NULL);
	}
	epollop->nfds = INITIAL_NFILES;

	evsignal_init(base);

	return (epollop);
}

3.2 event_set()

3.2.1 函数定义

void event_set(struct event *ev, int fd, short events, void (*callback)(int, short, void *), void *arg)

3.2.2 函数说明

event_set 初始化事件event,设置回调函数和关注的事件。
参数说明:
ev:执行要初始化的event对象;
fd:该event绑定的“句柄”,对于信号事件,它就是关注的信号;
events:在该fd上关注的事件类型,它可以是EV_READ, EV_WRITE, EV_SIGNAL;
callback:这是一个函数指针,当fd上的事件event发生时,调用该函数执行处理,它有三个参数,调用时由event_base负责传入,按顺序,实际上就是event_set时的fd, event和arg;
arg:传递给callback函数指针的参数;

定时事件说明:evtimer_set(&ev, timer_cb, NULL) = event_set(&ev, -1, 0, timer_cb, NULL)
由于定时事件不需要fd,并且定时事件是根据添加时(event_add)的超时值设定的,因此这里event也不需要设置。
这一步相当于初始化一个event handler,在libevent中事件类型保存在event结构体中。
注意:libevent并不会管理event事件集合,这需要应用程序自行管理;

3.2.3 函数源码分析

1 struct event结构体分析。

struct event {
	TAILQ_ENTRY (event) ev_next;
	TAILQ_ENTRY (event) ev_active_next;
	TAILQ_ENTRY (event) ev_signal_next;
	unsigned int min_heap_idx;	/* for managing timeouts 用于管理超时默认是-1 */

	struct event_base *ev_base; //属于哪个一event_base

	int ev_fd; //设置事件监听对象,也就是监听句柄
	short ev_events; //设置监听对象触发的动作:EV_READ, EV_WRITE, EV_SIGNAL,EV_TIMEOUT,EV_PERSIST  
	short ev_ncalls; //事件被调用了几次
	short *ev_pncalls;	/* Allows deletes in callback */

	struct timeval ev_timeout;

	int ev_pri;		/* smaller numbers are higher priority */
    
    //设置事件的回调函数
	void (*ev_callback)(int, short, void *arg);

    //设置事件的回调函数的参数
	void *ev_arg;

	int ev_res;		/* result passed to event callback */
	int ev_flags; //事件的状态,EVLIST_INIT,EVLIST_INTERNAL,EVLIST_ACTIVE,EVLIST_SIGNAL,EVLIST_INSERTED,EVLIST_TIMEOUT
};

2 event_set源码分析

void
event_set(struct event *ev, int fd, short events,
	  void (*callback)(int, short, void *), void *arg)
{
	/* Take the current base - caller needs to set the real base later */

    //设置当前的event_base,默认是current_base,后面可以通过event_base_set重新设置
	ev->ev_base = current_base;

    //设置事件的回调函数
	ev->ev_callback = callback;
    //设置事件的回调函数的参数
	ev->ev_arg = arg;

    //设置事件监听对象,也就是监听句柄
	ev->ev_fd = fd;

    //设置监听对象触发的动作:EV_READ, EV_WRITE, EV_SIGNAL
	ev->ev_events = events;
	ev->ev_res = 0;
	ev->ev_flags = EVLIST_INIT; //设置事件的状态
	ev->ev_ncalls = 0;
	ev->ev_pncalls = NULL;

	min_heap_elem_init(ev);

	/* by default, we put new events into the middle priority */
	if(current_base)
		ev->ev_pri = current_base->nactivequeues/2;
}

3.3 event_base_set()

3.3.1 函数定义

int event_base_set(struct event_base *base, struct event *ev) 

3.3.2 函数说明

设置event从属的event_base,这一步相当于指明event要注册到哪个event_base实例上。

3.3.3 函数源码分析

int
event_base_set(struct event_base *base, struct event *ev)
{
	/* Only innocent events may be assigned to a different base */
	if (ev->ev_flags != EVLIST_INIT)
		return (-1);

	ev->ev_base = base; //设置event的event_base
	ev->ev_pri = base->nactivequeues/2;

	return (0);
}

3.4 event_add()

3.4.1 函数定义

int event_add(struct event *ev, const struct timeval *tv)

3.4.2 函数说明

基本信息都已设置完成,只要简单的调用event_add()函数即可完成,其中tv是定时值;这一步相当于调用Reactor::register_handler()函数注册事件。

3.4.3 函数源码分析

int
event_add(struct event *ev, const struct timeval *tv)
{
	struct event_base *base = ev->ev_base; //自己事件所属的event_base
	const struct eventop *evsel = base->evsel; //示选择的事件模型,这里默认是epllo
	void *evbase = base->evbase;////事件模型存储数据的地址指针
	int res = 0;

    // tv不为0设置了,超时事件
	if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) {
		if (min_heap_reserve(&base->timeheap,
			1 + min_heap_size(&base->timeheap)) == -1)
			return (-1);  /* ENOMEM == errno */
	}

    //1将ev存放到evsel事件模型的数据evbase中,2 将ev插入到&base->eventqueue的末尾
	if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
	    !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {

        //将ev添加到evbase(epoll:epollop)的fds中(这个地方很重要,相当调用了epoll_add)
		res = evsel->add(evbase, ev);
		if (res != -1)
			event_queue_insert(base, ev, EVLIST_INSERTED); //添加到base->eventqueue的末尾
	}	
}

epoll_add 函数中,主要是向内核注册监听事件。

static int
epoll_add(void *arg, struct event *ev)
{
	struct epollop *epollop = arg;
	struct epoll_event epev = {0, {0}};
	struct evepoll *evep;
	int fd, op, events;

	if (ev->ev_events & EV_SIGNAL)
		return (evsignal_add(ev));

	fd = ev->ev_fd;
	if (fd >= epollop->nfds) {
		/* Extent the file descriptor array as necessary */
		if (epoll_recalc(ev->ev_base, epollop, fd) == -1)
			return (-1);
	}

    //给fd分配一个epollop。hash[fd] = evep。
	evep = &epollop->fds[fd];
	op = EPOLL_CTL_ADD;
	events = 0;

    //读
	if (evep->evread != NULL) {
		events |= EPOLLIN;
		op = EPOLL_CTL_MOD;
	}

    //写
	if (evep->evwrite != NULL) {
		events |= EPOLLOUT;
		op = EPOLL_CTL_MOD;
	}

    //读事件
	if (ev->ev_events & EV_READ)
		events |= EPOLLIN;
    //写事件
	if (ev->ev_events & EV_WRITE)
		events |= EPOLLOUT;

	epev.data.fd = fd;
	epev.events = events;
    /* epoll_ctl:epoll的事件注册函数
     * epollop->epfd:由 epoll_create 生成的epoll专用的文件描述符
     * op:要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修 改、EPOLL_CTL_DEL 删除
     * ev->ev_fd:监听的对象,也就是监听事件的句柄。
     * epev:指向epoll_event的指针。主要包含2部分信息:epev.data.fd = 监听的对象。
     * epev.events = events监听对象的事件类型读或者写。
     */ 
	if (epoll_ctl(epollop->epfd, op, ev->ev_fd, &epev) == -1)
			return (-1);

	/* Update events responsible */
	if (ev->ev_events & EV_READ)
		evep->evread = ev; //读回调处理ev
	if (ev->ev_events & EV_WRITE)
		evep->evwrite = ev; //写回调处理ev

	return (0);
}

3.5 event_base_loop()

3.5.1 函数定义

int event_base_loop(struct event_base *base, int flags) 

3.5.2 函数说明

程序进入无限循环,等待就绪事件并执行事件处理
event_base *base:event_base
flags:超时时间。

3.5.3 函数源码分析

1 event_base_loop,主要是通过evsel->dispatch(base, evbase, tv_p),真实调用了epoll_dispatch(),epoll_dispatch()函数中要是是调用了内核的epoll_wait,epoll_wait是阻塞等待关注的有事件发生。如果有关注时间的发生之后调用event_active将事件写入到event_base的激活事件队列中。如果有激活的队列,调用event_process_active(base)去执行激活的队列。

int
event_base_loop(struct event_base *base, int flags)
{
	const struct eventop *evsel = base->evsel;//网络I/O模型,epoll
	void *evbase = base->evbase;//模型中数据存储,epollop
	struct timeval tv;

	struct timeval *tv_p;
	int res, done;

	/* clear time cache */
	base->tv_cache.tv_sec = 0;

	if (base->sig.ev_signal_added)
		evsignal_base = base;
	done = 0;
	while (!done) {
				
		/*没有注册的事件,就退出 */
		if (!event_haveevents(base)) {
			event_debug(("%s: no events registered.", __func__));
			return (1);
		}

        // 调用系统I/O demultiplexer等待就绪I/O events,可能是epoll_wait,或者select等;
        // 在evsel->dispatch()中,会把就绪signal event、I/O event插入到激活链表中
        //epoll模型,主要调用的是epoll_dispatch
		res = evsel->dispatch(base, evbase, tv_p);

		if (res == -1)
			return (-1);
		gettime(base, &base->tv_cache);

		timeout_process(base);

        // 调用event_process_active()处理激活链表中的就绪event,调用其回调函数执行事件处理
        // 该函数会寻找最高优先级(priority值越小优先级越高)的激活事件链表,
        // 然后处理链表中的所有就绪事件;
        // 因此低优先级的就绪事件可能得不到及时处理; 
		if (base->event_count_active) {
			event_process_active(base);
			if (!base->event_count_active && (flags & EVLOOP_ONCE))
				done = 1;
		} else if (flags & EVLOOP_NONBLOCK)
			done = 1;
	}

	/* clear time cache */
	base->tv_cache.tv_sec = 0;

	
	return (0);
}

2 epoll_dispatch()函数,主要是函数中要是是调用了内核的epoll_wait,epoll_wait是阻塞等待关注的有事件发生。如果有关注时间的发生之后调用event_active将事件写入到event_base的激活事件列表中。

static int
epoll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
{
	struct epollop *epollop = arg;
	struct epoll_event *events = epollop->events;
	struct evepoll *evep;
	int i, res, timeout = -1;

	if (tv != NULL)
		timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;

	if (timeout > MAX_EPOLL_TIMEOUT_MSEC) {
		/* Linux kernels can wait forever if the timeout is too big;
		 * see comment on MAX_EPOLL_TIMEOUT_MSEC. */
		timeout = MAX_EPOLL_TIMEOUT_MSEC;
	}

    /* 等待事件触发,当超过timeout还没有事件触发时,就超时
     * epfd:由epoll_create 生成的epoll专用的文件描述符;
     * epoll_event:用于回传代处理事件的数组;
     * maxevents:每次能处理的事件数;
     * timeout:等待I/O事件发生的超时值;-1相当于阻塞,0相当于非阻塞。一般用-1即可
     * res:返回发生事件数
     *
     * epoll_wait工作原理:
     * poll_wait运行的原理是
     * 等侍注册在epfd上的socket fd的事件的发生,如果发生则将发生的sokct fd和事件类型放入到events数组中。
     * 并 且将注册在epfd上的socket fd的事件类型给清空,所以如果下一个循环你还要关注这个socket fd的话
     * ,则需要用epoll_ctl(epfd,EPOLL_CTL_MOD,listenfd,&ev)来重新设置socket fd的事件类型。
     * 这时不用EPOLL_CTL_ADD,因为socket fd并未清空,只是事件类型清空。这一步非常重要。
     */
	res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout);

	if (res == -1) {
		if (errno != EINTR) {
			event_warn("epoll_wait");
			return (-1);
		}

		evsignal_process(base);
		return (0);
	} else if (base->sig.evsignal_caught) {
		evsignal_process(base);
	}


	for (i = 0; i < res; i++) {
		int what = events[i].events;
		struct event *evread = NULL, *evwrite = NULL;
		int fd = events[i].data.fd; //拿到触发事件的socketId

		if (fd < 0 || fd >= epollop->nfds)
			continue;

        //从epollop->fds[fd]找到回调evep。epollop->fds[fd] = evep,hash结构。
		evep = &epollop->fds[fd];

        //获取读写回调event
		if (what & (EPOLLHUP|EPOLLERR)) {
			evread = evep->evread;
			evwrite = evep->evwrite;
		} else {
			if (what & EPOLLIN) {
				evread = evep->evread;
			}

			if (what & EPOLLOUT) {
				evwrite = evep->evwrite;
			}
		}

		if (!(evread||evwrite))
			continue;

        //将读写放到激活队列中
		if (evread != NULL)
			event_active(evread, EV_READ, 1);
		if (evwrite != NULL)
			event_active(evwrite, EV_WRITE, 1);
	}

    //扩容
	if (res == epollop->nevents && epollop->nevents < MAX_NEVENTS) {
		/* We used all of the event space this time.  We should
		   be ready for more events next time. */
		int new_nevents = epollop->nevents * 2;
		struct epoll_event *new_events;

		new_events = realloc(epollop->events,
		    new_nevents * sizeof(struct epoll_event));
		if (new_events) {
			epollop->events = new_events;
			epollop->nevents = new_nevents;
		}
	}

	return (0);
}

3 event_active函数,主要是讲激活的event加入激活队列中:base->activequeues。

//放入到激活队列中,ev:事件,res:EV_READ|EV_WRITE,ncalls:调用次数:1
void
event_active(struct event *ev, int res, short ncalls)
{
	/* We get different kinds of events, add them together */
	if (ev->ev_flags & EVLIST_ACTIVE) {
		ev->ev_res |= res;
		return;
	}

	ev->ev_res = res;
	ev->ev_ncalls = ncalls;
	ev->ev_pncalls = NULL;
	event_queue_insert(ev->ev_base, ev, EVLIST_ACTIVE);
}

4 event_process_active主要是处理激活队列中的数据

static void
event_process_active(struct event_base *base)
{
	struct event *ev;
	struct event_list *activeq = NULL;
	int i;
	short ncalls;

    ////获得就绪链表中有就绪事件并且高优先级的表头
	for (i = 0; i < base->nactivequeues; ++i) {
		if (TAILQ_FIRST(base->activequeues[i]) != NULL) {
			activeq = base->activequeues[i];
			break;
		}
	}

	assert(activeq != NULL);

	for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) {
		if (ev->ev_events & EV_PERSIST)
			event_queue_remove(base, ev, EVLIST_ACTIVE);
		else
			event_del(ev);//如果不是永久事件则需要进行一系统的删除工作,包括移除注册在事件链表的事件等

		/* Allows deletes to work */
		ncalls = ev->ev_ncalls;
		ev->ev_pncalls = &ncalls;
		while (ncalls) {
			ncalls--;
			ev->ev_ncalls = ncalls;
            //根据回调次数调用回调函数
			(*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg);
			if (event_gotsig || base->event_break)
				return;
		}
	}
}

你可能感兴趣的:(memcached,PHP程序员技术精粹)