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

  • 相关阅读:
    JavaScript 为字符串添加样式 【每日一段代码80】
    JavaScript replace()方法 【每日一段代码83】
    JavaScript for in 遍历数组 【每日一段代码89】
    JavaScript 创建用于对象的模板【每日一段代码78】
    html5 css3 新元素简单页面布局
    JavaScript Array() 数组 【每日一段代码88】
    JavaScript toUTCString() 方法 【每日一段代码86】
    位运算
    POJ 3259 Wormholes
    POJ 3169 Layout
  • 原文地址:https://www.cnblogs.com/ym65536/p/4889484.html
Copyright © 2011-2022 走看看