Libevent分析

一、使用举例

一个简单的使用libevent创建的echosvr如下:

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <unistd.h>
  5 #include <arpa/inet.h>
  6 #include <netinet/in.h>
  7 #include <sys/epoll.h>
  8 #include <sys/types.h>       
  9 #include <sys/socket.h>
 10 #include <errno.h>
 11 #include <event.h>
 12 #include "dlist.h"
 13 
 14 #define BUF_SIZE    1024
 15 char buf[BUF_SIZE];
 16 
 17 void on_accept(int sock, short event, void* arg);
 18 void on_read(int sock, short event, void* arg);
 19 
 20 struct event_list
 21 {
 22     struct event ev;
 23     struct list_head list;
 24 };
 25 
 26 struct event_list evlist;
 27 struct event_base* evbase = NULL;
 28 
 29 int main(void)
 30 {
 31     int listenfd = -1;
 32     struct sockaddr_in svraddr;
 33     int nready = 0;
 34     int i = 0;
 35     INIT_LIST_HEAD(&evlist.list); 
 36     
 37     if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
 38     {
 39         perror("socket error");
 40         return -1;
 41     }
 42 
 43     memset(&svraddr, 0, sizeof(svraddr));
 44     svraddr.sin_family = AF_INET;
 45     svraddr.sin_port = htons(44888);
 46     svraddr.sin_addr.s_addr = htonl(INADDR_ANY);
 47 
 48     if (bind(listenfd, (struct sockaddr* )&svraddr, sizeof(svraddr)) < 0)
 49     {
 50         perror("bind error");
 51         return -1;
 52     }
 53 
 54     if (listen(listenfd, 5) < 0)
 55     {
 56         perror("listen error");
 57         return -1;
 58     }
 59         
 60     evbase = event_base_new();
 61     struct event_list* evlisten = (struct event_list* )malloc(sizeof(struct event_list));
 62     list_add_tail(&(evlisten->list), &(evlist.list));
 63     event_set(&(evlisten->ev), listenfd, EV_READ | EV_PERSIST, on_accept, (void* )evbase);
 64     event_base_set(evbase, &(evlisten->ev));
 65     event_add(&(evlisten->ev), NULL);
 66 
 67     event_base_dispatch(evbase);
 68     printf("The End.
");
 69 
 70     return 0;
 71 }
 72 
 73 void on_accept(int sock, short event, void* arg)
 74 {
 75     int fd;
 76     struct event_base* evbase = (struct event_base* )arg;
 77     struct sockaddr_in cliaddr;
 78     uint32_t slen = sizeof(cliaddr);
 79     fd = accept(sock, (struct sockaddr* )&cliaddr, &slen);
 80     if (fd < 0)
 81     {
 82         perror("accept");
 83         return;
 84     }
 85     printf("recv sock %d: <%s:%d>
", fd, inet_ntoa(cliaddr.sin_addr), htons(cliaddr.sin_port));
 86 
 87     struct event_list* evread = (struct event_list* )malloc(sizeof(struct event_list));
 88     event_set(&(evread->ev), fd, EV_READ | EV_PERSIST, on_read, (void* )evread);
 89     event_base_set(evbase, &(evread->ev));
 90     event_add(&(evread->ev), NULL);
 91 }
 92 
 93 void on_read(int sock, short event, void* arg)
 94 {
 95     int rsize = 0;
 96     memset(buf, 0, BUF_SIZE);
 97     rsize = read(sock, buf, BUF_SIZE);
 98     if (rsize <= 0)
 99     {
100         if (rsize == 0)
101         {
102             printf("client %d close socket.
", sock);
103         }
104         else
105         {
106             perror("read:");
107         }
108         close(sock);
109         event_del((struct event* )arg);
110         free((struct event* )arg);
111         return;
112     }
113     printf("recv:%s
", buf);
114     write(sock, buf, strlen(buf));
115     printf("send:%s
", buf);
116 }

其中使用了双链表来管理events,相关代码如下:

  1 #ifndef _LIST_HEAD_H
  2 #define _LIST_HEAD_H
  3 
  4 // 双向链表节点
  5 struct list_head {
  6     struct list_head *next, *prev;
  7 };
  8 
  9 // 初始化节点:设置name节点的前继节点和后继节点都是指向name本身。
 10 #define LIST_HEAD_INIT(name) { &(name), &(name) }
 11 
 12 // 定义表头(节点):新建双向链表表头name,并设置name的前继节点和后继节点都是指向name本身。
 13 #define LIST_HEAD(name) 
 14     struct list_head name = LIST_HEAD_INIT(name)
 15 
 16 // 初始化节点:将list节点的前继节点和后继节点都是指向list本身。
 17 static inline void INIT_LIST_HEAD(struct list_head *list)
 18 {
 19     list->next = list;
 20     list->prev = list;
 21 }
 22 
 23 // 添加节点:将new插入到prev和next之间。
 24 static inline void __list_add(struct list_head *new,
 25                   struct list_head *prev,
 26                   struct list_head *next)
 27 {
 28     next->prev = new;
 29     new->next = next;
 30     new->prev = prev;
 31     prev->next = new;
 32 }
 33 
 34 // 添加new节点:将new添加到head之后,是new称为head的后继节点。
 35 static inline void list_add(struct list_head *new, struct list_head *head)
 36 {
 37     __list_add(new, head, head->next);
 38 }
 39 
 40 // 添加new节点:将new添加到head之前,即将new添加到双链表的末尾。
 41 static inline void list_add_tail(struct list_head *new, struct list_head *head)
 42 {
 43     __list_add(new, head->prev, head);
 44 }
 45 
 46 // 从双链表中删除entry节点。
 47 static inline void __list_del(struct list_head * prev, struct list_head * next)
 48 {
 49     next->prev = prev;
 50     prev->next = next;
 51 }
 52 
 53 // 从双链表中删除entry节点。
 54 static inline void list_del(struct list_head *entry)
 55 {
 56     __list_del(entry->prev, entry->next);
 57 }
 58 
 59 // 从双链表中删除entry节点。
 60 static inline void __list_del_entry(struct list_head *entry)
 61 {
 62     __list_del(entry->prev, entry->next);
 63 }
 64 
 65 // 从双链表中删除entry节点,并将entry节点的前继节点和后继节点都指向entry本身。
 66 static inline void list_del_init(struct list_head *entry)
 67 {
 68     __list_del_entry(entry);
 69     INIT_LIST_HEAD(entry);
 70 }
 71 
 72 // 用new节点取代old节点
 73 static inline void list_replace(struct list_head *old,
 74                 struct list_head *new)
 75 {
 76     new->next = old->next;
 77     new->next->prev = new;
 78     new->prev = old->prev;
 79     new->prev->next = new;
 80 }
 81 
 82 // 双链表是否为空
 83 static inline int list_empty(const struct list_head *head)
 84 {
 85     return head->next == head;
 86 }
 87 
 88 // 获取"MEMBER成员"在"结构体TYPE"中的位置偏移
 89 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
 90 
 91 // 根据"结构体(type)变量"中的"域成员变量(member)的指针(ptr)"来获取指向整个结构体变量的指针
 92 #define container_of(ptr, type, member) ({          
 93     const typeof( ((type *)0)->member ) *__mptr = (ptr);    
 94     (type *)( (char *)__mptr - offsetof(type,member) );})
 95 
 96 // 遍历双向链表
 97 #define list_for_each(pos, head) 
 98     for (pos = (head)->next; pos != (head); pos = pos->next)
 99 
100 #define list_for_each_safe(pos, n, head) 
101     for (pos = (head)->next, n = pos->next; pos != (head); 
102         pos = n, n = pos->next)
103 
104 #define list_entry(ptr, type, member) 
105     container_of(ptr, type, member)
106 
107 #endif
View Code

echosvr的代码流程:

1、经典的使用socket创建tcp socket。

2、创建event base:evbase = event_base_new();

3、创建listen event并加入到eventbase中:

    event_set(&(evlisten->ev), listenfd, EV_READ | EV_PERSIST, on_accept, (void* )evbase);
    event_base_set(evbase, &(evlisten->ev));
    event_add(&(evlisten->ev), NULL);

二、Libevent基本数据结构

1、struct event_base (事件处理框架结构)

 1 struct event_base 
 2 {
 3     const struct eventop *evsel;    // IO复用函数集, Linux下一般是epoll的封装
 4     void *evbase;
 5     int event_count;        /* counts number of total events */
 6     int event_count_active;    /* counts number of active events */
 7 
 8     int event_gotterm;        /* Set to terminate loop */
 9     int event_break;        /* Set to terminate loop immediately */
10 
11     /* active event management */
12     struct event_list **activequeues;
13     int nactivequeues;
14 
15     /* signal handling info */
16     struct evsignal_info sig;
17 
18     struct event_list eventqueue;    // 注册事件队列
19     struct timeval event_tv;
20 
21     struct min_heap timeheap;        // 管理定时器小根堆
22 
23     struct timeval tv_cache;        // 记录时间缓存
24 };

evsel:libevent支持Linux、Windows等多种平台,也支持epoll、poll、select、kqueue等多种I/O多路复用模型。evsel会指向

/* In order of preference */
static const struct eventop *eventops[] = {
#ifdef HAVE_EVENT_PORTS
    &evportops,
#endif
#ifdef HAVE_WORKING_KQUEUE
    &kqops,
#endif
#ifdef HAVE_EPOLL
    &epollops,
#endif
#ifdef HAVE_DEVPOLL
    &devpollops,
#endif
#ifdef HAVE_POLL
    &pollops,
#endif
#ifdef HAVE_SELECT
    &selectops,
#endif
#ifdef WIN32
    &win32ops,
#endif
    NULL
};

linux下会指向epollops。

2、struct event(代表一个事件)

 1 struct event 
 2 {
 3     TAILQ_ENTRY (event) ev_next;            // 已注册事件
 4     TAILQ_ENTRY (event) ev_active_next;     // 就绪事件
 5     TAILQ_ENTRY (event) ev_signal_next;        // 信号
 6     unsigned int min_heap_idx;    /* for managing timeouts */
 7 
 8     struct event_base *ev_base;
 9 
10     int ev_fd;                // 对于I/O事件,是绑定的文件描述符
11     short ev_events;        // event关注的事件类型
12     short ev_ncalls;        // 事件就绪执行时,调用 ev_callback 的次数
13     short *ev_pncalls;    /* Allows deletes in callback */
14 
15     struct timeval ev_timeout;    // timout事件的超时值
16 
17     int ev_pri;        /* smaller numbers are higher priority */
18 
19     void (*ev_callback)(int, short, void *arg);    // 回调函数
20     void *ev_arg;                                // 回调函数的参数
21 
22     int ev_res;        /* result passed to event callback */
23     int ev_flags;                                // event的状态
24 };

1) )ev_next,ev_active_next 和 ev_signal_next 都是双向链表节点指针, 定义如下:

1 #define TAILQ_ENTRY(type)                        
2 struct {                                
3     struct type *tqe_next;    /* next element */            
4     struct type **tqe_prev;    /* address of previous next element */    
5 }

2) ev_events:event关注的事件类型,它可以是以下3种类型: 
I/O事件: EV_WRITE和EV_READ 
定时事件:EV_TIMEOUT 
信号:    EV_SIGNAL 
辅助选项:EV_PERSIST,表明是一个永久事件

 三、libevent主要函数分析

1、event_base_new:创建事件处理框架

 1 struct event_base *event_base_new(void)
 2 {
 3     int i;
 4     struct event_base *base;
 5 // 为base分配堆内存
 6     if ((base = calloc(1, sizeof(struct event_base))) == NULL)
 7         event_err(1, "%s: calloc", __func__);
 8 // 信号相关
 9     event_sigcb = NULL;
10     event_gotsig = 0;
11 
12     detect_monotonic();
13     gettime(base, &base->event_tv);
14     
15     min_heap_ctor(&base->timeheap);
16     TAILQ_INIT(&base->eventqueue);
17     base->sig.ev_signal_pair[0] = -1;
18     base->sig.ev_signal_pair[1] = -1;
19 
20 // 具体底层IO操作函数, 实际使用epoll封装    
21     base->evbase = NULL;
22     for (i = 0; eventops[i] && !base->evbase; i++) {
23         base->evsel = eventops[i];
24 
25         base->evbase = base->evsel->init(base);
26     }
27 
28     if (base->evbase == NULL)
29         event_errx(1, "%s: no event mechanism available", __func__);
30 
31     if (evutil_getenv("EVENT_SHOW_METHOD")) 
32         event_msgx("libevent using: %s
",
33                base->evsel->name);
34 
35     /* allocate a single active event queue */
36     event_base_priority_init(base, 1);
37 
38     return (base);
39 }

函数首先申请base的内存,第16行TAILQ_INIT(&base->eventqueue);是初始化注册事件,TAILQ_INIT是一个宏定义:

1 #define    TAILQ_INIT(head) do {                        
2     (head)->tqh_first = NULL;                    
3     (head)->tqh_last = &(head)->tqh_first;                
4 } while (0)

tqh_first为空,tqh_last指向tqh_first的地址,完成一个空的双指针的初始化。

22-26行是注册底层IO事件操作函数集,这里指向的是epollops,有如下操作:

1 const struct eventop epollops = {
2     "epoll",
3     epoll_init,
4     epoll_add,
5     epoll_del,
6     epoll_dispatch,
7     epoll_dealloc,
8     1 /* need reinit */
9 };

epoll相关的操作可以参考http://www.cnblogs.com/ym65536/p/4854869.html, 这里的操作只是对epoll做了简单的封装。

2、event_set

 1 void event_set(struct event *ev, int fd, short events,
 2       void (*callback)(int, short, void *), void *arg)
 3 {
 4     /* Take the current base - caller needs to set the real base later */
 5     ev->ev_base = current_base;
 6 
 7     ev->ev_callback = callback;
 8     ev->ev_arg = arg;
 9     ev->ev_fd = fd;
10     ev->ev_events = events;
11     ev->ev_res = 0;
12     ev->ev_flags = EVLIST_INIT;
13     ev->ev_ncalls = 0;
14     ev->ev_pncalls = NULL;
15 
16     min_heap_elem_init(ev);
17 
18     /* by default, we put new events into the middle priority */
19     if(current_base)
20         ev->ev_pri = current_base->nactivequeues/2;
21 }

这个函数很简单,只是绑定了事件event和描述符、回调函数等信息。

3、event_base_set

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;
    ev->ev_pri = base->nactivequeues/2;

    return (0);
}

绑定事件event和事件处理框架event_base

4、event_add

原文地址:https://www.cnblogs.com/ym65536/p/4889484.html