zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    BZOJ4036 HAOI2015按位或(概率期望+容斥原理)
    洛谷p2661信息传递题解
    洛谷P1434滑雪题解及记忆化搜索的基本步骤
    二分图最大匹配
    线段树

    图论基本算法
    并查集
    RMQ--ST表
    矩阵快速幂和矩阵乘法
  • 原文地址:https://www.cnblogs.com/ym65536/p/4889484.html
Copyright © 2011-2022 走看看