一、使用举例
一个简单的使用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
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