libevent源码分析

1、入门

1.1、概述
Libevent是一个用于开发可扩展性网络服务器的基于事件驱动(event-driven)模型的网络库。Libevent有几个显著的亮点:
(1)事件驱动(event-driven),高性能;
(2)轻量级,专注于网络,不如 ACE 那么臃肿庞大;
(3)源代码相当精炼、易读;
(4)跨平台,支持 Windows、Linux、*BSD和 Mac Os;
(5)支持多种 I/O多路复用技术, epoll、poll、dev/poll、select 和kqueue 等;
(6)支持 I/O,定时器和信号等事件;
(7)注册事件优先级;

 Libevent 已经被广泛的应用,作为底层的网络库;比如 memcached、 Vomi t、 Nylon、 Netchat等等。

1.2、一个简单示例

#include <event.h>

int lasttime;

static void
timeout_cb(int fd, short event, void *arg)
{
    struct timeval tv;
    struct event *timeout = arg;
    int newtime = time(NULL);

    //printf("%s: called at %d: %d\n", __func__, newtime,
    printf("%s: called at %d: %d\n", "timeout_cb", newtime,
        newtime - lasttime);
    lasttime = newtime;

    evutil_timerclear(&tv);
    tv.tv_sec = 2;
    //重新注册event
    event_add(timeout, &tv);
}

int
main (int argc, char **argv)
{
    struct event timeout;
    struct timeval tv;

    /* Initalize the event library */
    //初始化event环境
    event_init();

    /* Initalize one event */
    //设置事件
    evtimer_set(&timeout, timeout_cb, &timeout);

    evutil_timerclear(&tv);
    tv.tv_sec = 2;
    //注册事件
    event_add(&timeout, &tv);

    lasttime = time(NULL);
    //等待,分发,处理事件
    event_dispatch();

    return (0);
}

这是一个简单的基于libevent的定时器程序,运行结果:

用libevent编程非常简单,只需要调用event_init初始化环境,然后调用event_add注册相应的事件,接着调用event_dispatch等待并处理相应的事件即可。

在编程时,我们通常能看到代码里没有调用 event_init,其实源码可知event_init主要是创建一个event_base的变量返回,并把当前event_base指针赋值给一个全局变量。

所以在编码中我们通常自己去创建一个event_base.

struct event_base *base;
base = event_base_new();

1.3、源代码结构
Libevent 的源代码虽然都在一层文件夹下面,但是其代码分类还是相当清晰的,主要可分为头文件、内部使用的头文件、辅助功能函数、日志、libevent 框架、对系统 I/O 多路复用机制的封装、信号管理、定时事件管理、缓冲区管理、基本数据结构和基于 libevent的两个实用库等几个部分,有些部分可能就是一个源文件。
(1)头文件
主要就是 event.h:事件宏定义、接口函数声明,主要结构体 event 的声明;
(2)内部头文件
xxx-internal.h:内部数据结构和函数,对外不可见,以达到信息隐藏的目的;
(3)libevent框架
event.c:event 整体框架的代码实现;
(4)对系统 I/O多路复用机制的封装
epoll.c:对 epoll 的封装;
select.c:对 select 的封装;
devpoll.c:对 dev/poll 的封装;
kqueue.c:对kqueue 的封装;
(5)定时事件管理
min-heap.h:其实就是一个以时间作为 key的小根堆结构;
(6)信号管理
signal.c:对信号事件的处理;
(7)辅助功能函数
evutil.h  和 evutil.c:一些辅助功能函数,包括创建 socket pair和一些时间操作函数:加、减和比较等。
(8)日志
log.h和 log.c:log 日志函数
(9)缓冲区管理
evbuffer.c 和buffer.c:libevent 对缓冲区的封装;
(10)基本数据结构
compat\sys 下的两个源文件: queue.h是 libevent 基本数据结构的实现,包括链表,双向链表,队列等;_libevent_time.h:一些用于时间操作的结构体定义、函数和宏定义;
(11)实用网络库
     http 和evdns:是基于 libevent 实现的http 服务器和异步 dns 查询库;

2、核心对象
结构体event和event_base是libevent的两个核心数据结构,前者代表一个事件对象,后者代表整个事件处理框架。

2.1、event(事件)

//event.h
struct event {
    TAILQ_ENTRY (event) ev_next;          //已注册事件链表
    TAILQ_ENTRY (event) ev_active_next;   //就绪事件链表
    TAILQ_ENTRY (event) ev_signal_next;   //signal链表
    unsigned int min_heap_idx;            /* for managing timeouts,事件在堆中的下标 */  

    struct event_base *ev_base;

    int ev_fd;                           //对于I/O事件,是绑定的文件描述符;对于signal事件,是绑定的信号
    short ev_events;                     //event关注的事件类型
    short ev_ncalls;                     //事件就绪执行时,调用 ev_callback 的次数
    short *ev_pncalls;                   /* Allows deletes in callback */

    struct timeval ev_timeout;           //timout事件的超时值

    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;                        //event的状态
};
Libevent通过event对象将I/O事件、信号事件和定时器事件封装,从而统一处理,这也是libevent的精妙所有。
各个字段的具体含义:
(1) ev_events:event关注的事件类型,它可以是以下3种类型:
I/O事件: EV_WRITE和EV_READ
定时事件:EV_TIMEOUT
信号:    EV_SIGNAL
辅助选项:EV_PERSIST,表明是一个永久事件
libevent中的定义为:
#define EV_TIMEOUT    0x01
#define EV_READ    0x02
#define EV_WRITE    0x04
#define EV_SIGNAL    0x08
#define EV_PERSIST    0x10    /* Persistant event */
(2)ev_next,ev_active_next 和 ev_signal_next 都是双向链表节点指针;它们是 libevent 对不同事件类型和在不同的时期,对事件的管理时使用到的字段。
libevent 使用双向链表保存所有注册的 I/O和 Signal 事件,ev_next 就是该I/O事件在链表中的位置;此链表可以称为“已注册事件链表”;
同样 ev_signal_next 就是 signal 事件在 signal 事件链表中的位置;
ev_active_next:libevent 将所有的激活事件放入到链表 active list 中,然后遍历 active list 执
行调度,ev_active_next就指明了 event 在active list 中的位置;
(3)min_heap_idx 和 ev_timeout,如果是 timeout 事件,它们是 event 在小根堆中的索引和超时值,libevent 使用小根堆来管理定时事件。
(4)ev_base指向事件框架实例。
(5)ev_fd,对于 I/O事件,是绑定的文件描述符;对于 signal 事件,是事件对应的信号;
(6)eb_flags:libevent 用于标记 event信息的字段,表明事件当前的状态,可能的值有:
#define EVLIST_TIMEOUT   0x01 // event在time堆中
#define EVLIST_INSERTED 0x02 // event在已注册事件链表中
#define EVLIST_SIGNAL    0x04 // 未见使用
#define EVLIST_ACTIVE    0x08 // event在激活链表中
#define EVLIST_INTERNAL 0x10 // 内部使用标记
#define EVLIST_INIT      0x80 // event 已被初始化

2.2、event_base(事件处理框架)
//event_internal.h
struct event_base {
    const struct eventop *evsel; //底层具体I/O demultiplex操作函数集
    void *evbase;
    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;
    int nactivequeues;          //就绪事件队列个数
              
    /* signal handling info */
    struct evsignal_info sig;   //用于管理信号
               
    struct event_list eventqueue; //注册事件队列 
    struct timeval event_tv;
                 
    struct min_heap timeheap;   //管理定时器的小根堆
    struct timeval tv_cache;    //记录时间缓存
};

(1)evsel:libevent支持Linux、Windows等多种平台,也支持epoll、poll、select、kqueue等多种I/O多路复用模型。如果把event_init、event_add看成高层抽象的统一事件操作接口,则evsel为这些函数在底层具体的I/O demultiplex的对应的操作函数集。eventop为函数指针的集合:

struct eventop {
    const char *name;
    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;
}; 
在初始化函数event_base_new中,libevent将evsel指向全局数组eventops的具体元素:

//event.c I/O multiplex机制实例数组
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     
};
2.3、主要函数
2.3.1、event_int(初始化libevent实例)
struct event_base *

event_init(void);
初始化事件处理框架实例,内部调用event_base_new。

event_base_new的主要逻辑:

struct event_base *
event_base_new(void)
{    
          
    //初始化小根堆
    min_heap_ctor(&base->timeheap);
           
    //初始化注册事件队列 
    TAILQ_INIT(&base->eventqueue);
            
    for (i = 0; eventops[i] && !base->evbase; i++) {
        //I/O demultiplex机制实例
        base->evsel = eventops[i];
                  
        //初始化I/O demultiplex实例(参见win32_init)
        base->evbase = base->evsel->init(base);
    }              
             
    //分配1个就绪事件队列 
    event_base_priority_init(base, 1);
}             
2.3.2、event_add(注册事件)
//注册事件
int
event_add(struct event *ev, const struct timeval *tv)
该函数主要将事件ev加入到事件框架event_base的注册事件链表base->eventqueue。

2.3.3、event_del(删除事件)
//删除事件
int
event_del(struct event *ev)
该函数主要将事件ev从相应的链表上删除。

2.3.4、event_set(设置事件)
/*设置event对象
 **ev:事件对象 
 **fd:事件对应的文件描述符或信号,对于定时器设为-1
 **events:事件类型,比如 EV_READ,EV_PERSIST, EV_WRITE, EV_SIGNAL
 **callback:事件的回调函数 
 **arg:回调函数参数 
 */
void
event_set(struct event *ev, evutil_socket_t fd, short events,
    void (*callback)(evutil_socket_t, short, void *), void *arg) 
{
    int r;
    r = event_assign(ev, current_base, fd, events, callback, arg);
    EVUTIL_ASSERT(r == 0);
} 

在将事件注册事件处理框架之前,应该先调用event_set对事件进行相关设置。


2.4、libevent对event的管理
event结构有3个链表结点域和一个小根堆索引,libevent通过3个链表和一个小根堆对I/O事件、signal事件和timer事件进行管理。
对于I/O事件,通过event_add将其加入event_base的注册事件链表eventqueue ;就绪时会加入event_base的就绪链表activequeues[];
对于timer事件,event_add将其加入到event_base的小根堆timeheap;
Signale事件的管理相对复杂些,event_add将其加入到注册事件链表,同时,event_add内部会调用I/O demultiplex的add函数(对于I/O事件也一样),比如epoll_add。而add函数又会调用evsignal_add将其加入到evsignal_info的evsigevents[signo]链表(关于signal,后面会详细介绍)。


3、事件处理框架主循环
Libevent将I/O事件、signal事件和timer事件用统一的模型进行处理,这是非常精妙的。libevent主循环函数不断检测注册事件,如果有事件发生,则将其放入就绪链表,并调用事件的回调函数,完成业务逻辑处理。
3.1、event_dispatch
//事件处理主循环
int
event_dispatch(void)
这是呈现给外部的接口,它的实现很简单,即调用event_loop,而event_loop调用event_base_loop,event_base_loop完成实际的主循环逻辑。

3.2、event_base_loop
主要算法:
done = 0;
while (!done) {
      
    /*如果没有就绪事件,根据timer heap中事件的最小超时时间,计算I/O demultiplex的
     **最大等待时间. 相反,如果有就绪事件,则清除tv,即I/O demultiplex不应该等待. 
     */   
    if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK)) { 
        timeout_next(base, &tv_p);
    } else {    
        /*               
         * if we have active events, we just poll new events
         * without waiting.             
         */                                   
        evutil_timerclear(&tv);
    }                     
       
    /* If we have no events, we just exit */ 
    //没有事件处理,则退出循环 
    if (!event_haveevents(base)) {
        event_debug(("%s: no events registered.", __func__));
        return (1);  
    }                 
        
    //tv_p为I/O demultiplex的超时时间
    //处理signal事件和I/O事件
    res = evsel->dispatch(base, evbase, tv_p);
         
    //处理timeout事件,对于超时的事件,将其放到就绪事件链表 
    timeout_process(base);
          
    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;                 
                          
}//end while 

3.3、timeout_next
/*根据timer heap中事件的最小超时时间,计算I/O demultiplex的最大等待时间.
 **为了及时处理timer事件,I/O demultiplex的最大等待时间不应该超过timer事件中最小的超时时间,
 **否则,timer事件就不能得到及时处理 
 */
static int 
timeout_next(struct event_base *base, struct timeval **tv_p)
{    
    struct timeval now;
    struct event *ev;
    struct timeval *tv = *tv_p;
                         
    //如果没有timer事件,则直接返回 
    if ((ev = min_heap_top(&base->timeheap)) == NULL) {
        /* if no time-based events are active wait for I/O */ 
        *tv_p = NULL;                     
        return (0);                                
    }                                                   
                              
    if (gettime(base, &now) == -1)
        return (-1);                           
                                   
    //如果最小的timer事件已经超时,则清除tv,即I/O demultiplex不应该等待.
    if (evutil_timercmp(&ev->ev_timeout, &now, <=)) {
        evutil_timerclear(tv);                      
        return (0);                                          
    }                                                             
                                        
    //更新I/O demultiplex可以等待的最大时间:ev->ev_timeout - now
    evutil_timersub(&ev->ev_timeout, &now, tv);
    return (0);                                  
}

3.4、dispatch函数
调用底层I/O multiplex的dispatch函数,具体的实现可以参见epoll的实现epoll_dispatch。

3.5、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--; //调用次数减1                                                                           
            ev->ev_ncalls = ncalls;                                                                                        
            //调用事件的回调函数                                                                                                        
            (*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg);                                                                 
            if (base->event_break)                                                                                                                   
                return;                                                                              
        }                                                                          
    }                                                                                             
}      
4、Timer事件
Timer事件的处理本身比较简单,不再赘述。

5、signal事件
5.1、socket pair
Libevent通过socketpair,将signal事件与I/O事件完美的统一起来。Socketpair,简单的说就一对socket,一端用于写,一端用于读。工作方式如下: 为了与I/O事件统一起来,libevent内部使用了一个针对read socket的读事件。

5.1.1、Socketpair的创建
与信号事件的初始化工作都是在evsignal_init中完成的,而evsignal_init通过调用evutil_socketpair创建socketpair。对于Unix平台,有socketpair系统调用;对于Windows,则相对复杂一些,具体见evutil_socketpair函数的实现。

5.2、evsignal_info
在event_base内部有一个evsignal_info类型的字段sig,它是用于管理signal事件的核心数据结构
//evsignal.h
struct evsignal_info {
    struct event ev_signal;    //内部socket读事件
    int ev_signal_pair[2];  //对应socket pair的两个socket描述符 
    int ev_signal_added;  //内部socket读事件是否已经加入注册链表
    volatile sig_atomic_t evsignal_caught; //是否有信号发生 
    //信号事件链表数组,evsigevents[signo]表示注册信号signo的事件
    struct event_list evsigevents[NSIG]; 
    //具体记录每个信号触发的次数,evsigcaught[signo]是记录信号 signo被触发的次数 
    sig_atomic_t evsigcaught[NSIG];
                
    //sh_old记录了原来的 signal 处理函数指针,当信号 signo 注册的 event 被清空时,需要重新设置其处理函数
#ifdef HAVE_SIGACTION
    struct sigaction **sh_old;
#else        
    ev_sighandler_t **sh_old;
#endif        
    int sh_old_max;
};  

5.3、主要函数
5.3.1、evsignal_init
主要完成evsignal_info的初始化,主要算法:
int
evsignal_init(struct event_base *base)
{    
    evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair);
    base->sig.sh_old = NULL;
    base->sig.sh_old_max = 0;
             
    //事件发生次数设为0
    base->sig.evsignal_caught = 0;
    memset(&base->sig.evsigcaught, 0, sizeof(sig_atomic_t)*NSIG);
    /* initialize the queues for all events */
    for (i = 0; i < NSIG; ++i) 
        TAILQ_INIT(&base->sig.evsigevents[i]);
                
    evutil_make_socket_nonblocking(base->sig.ev_signal_pair[0]); //写端
                 
    //设置内部读事件 
    event_set(&base->sig.ev_signal, base->sig.ev_signal_pair[1], 
        EV_READ | EV_PERSIST, evsignal_cb, &base->sig.ev_signal); //读端
    base->sig.ev_signal.ev_base = base;
                   
    //sig.ev_signal == EV_READ | EV_PERSIST | EVLIST_INTERNAL
    base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL;
}        

该函数的关键在于这里会设置libevent用于管理信号事件的内部读事件evsignal_info的ev_signal,并将该事件对应的文件描述符设为socket pair的读端。该函数由I/O multiplex的init函数调用。注:这里只是设置,而并没有注册socket pair的读事件(见下一节)。

5.3.2、evsignal_add
当调用event_add注册信号事件时,内部会先调用I/O multiplex的add函数,add函数又会调用evsignal_add,将事件加到evsignal_info内部的信号事件链表。然后再event_queue_insert将其添加到event_base的注册事件链表。

int
evsignal_add(struct event *ev)
{    
    int evsignal; 
    struct event_base *base = ev->ev_base; 
    struct evsignal_info *sig = &ev->ev_base->sig;
                         
    //信号事件与I/O事件是不相容的
    if (ev->ev_events & (EV_READ|EV_WRITE))
        event_errx(1, "%s: EV_SIGNAL incompatible use", __func__);
    //事件的信号                  
    evsignal = EVENT_SIGNAL(ev);  
    assert(evsignal >= 0 && evsignal < NSIG);
    if (TAILQ_EMPTY(&sig->evsigevents[evsignal])) {
        event_debug(("%s: %p: changing signal handler", __func__, ev));
                                                          
        //注册信号处理函数evsignal_handler                        
        //向socket pair的写端写入一个字节的数据                   
        if (_evsignal_set_handler(                                
                base, evsignal, evsignal_handler) == -1)                                   
            return (-1);                                                           
                                                                   
        /* catch signals if they happen quickly */                         
        evsignal_base = base;                                              
                                                                            
        //添加内部socket读事件sig->ev_signal                                        
        if (!sig->ev_signal_added) {                                                
            if (event_add(&sig->ev_signal, NULL))                                                    
                return (-1);                                                                                              
            sig->ev_signal_added = 1;                                                                             
        }                                                                                                                  
    }                                                                                    
                                             
    /* multiple events may listen to the same signal */
    //添加信号事件链表                           
    TAILQ_INSERT_TAIL(&sig->evsigevents[evsignal], ev, ev_signal_next);
                                                  
    return (0);                                       
}   
这里有两个地方需要注意,一是调用_evsignal_set_handler设置外部注册信号事件对应的信号的信号处理函数evsignal_handler:

static void
evsignal_handler(int sig)
{    
    int save_errno = errno;
               
    //设置信号事件的发生次数 
    evsignal_base->sig.evsigcaught[sig]++;
    evsignal_base->sig.evsignal_caught = 1;
                         
#ifndef HAVE_SIGACTION   
    signal(sig, evsignal_handler);
#endif                        
                              
    /* Wake up our notification mechanism */ 
    //向socket pair的写端写数据   
    send(evsignal_base->sig.ev_signal_pair[0], "a", 1, 0);
    errno = save_errno;                
}
当用户注册信号事件对应的信号发生时,OS转到evsignal_handler函数,从而设置sig.evsignal_caught,并向socket pair的写端发送数据。
二是通过调用event_add完成内部socket pair的读事件sig->ev_signal的注册。最后,将(外部)事件添加到信号事件链表。
5.3.2、与主循环结合
信号事件完成了注册,libevent就会在主循环中,等待事件发生,并处理事件。为了理解,来看看具体I/O demultiplex的dispatch函数:

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;           
    }                                                    
                                        
    //等待I/O事件                           
    res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout);
                                             
    if (res == -1) {                             
        if (errno != EINTR) {                                 
            event_warn("epoll_wait");                                          
            return (-1);                                                                    
        }                                                                                            
        //epoll_wait被信号中断                                         
        evsignal_process(base);                                        
        return (0);                                                             
    } else if (base->sig.evsignal_caught) {//发生了信号事件                          
        //处理信号事件                                                                            
        evsignal_process(base);                                                                   
    }                                                                                                  
    //…                                           
}         

epoll_dispatch函数调用epoll_wait函数等待I/O发生。然后,如果有信号事件发生,则调用evsignal_process处理信号事件,evsignal_process的逻辑比较简单,它只是将事件从注册事件链表转移到就绪事件链表。

还记得evsignal_handler函数吗?它是所有(外部)信号事件对应的信号的信号处理函数,将实际的信号发生时,OS会转而执行evsignal_handler函数,而它便向socket pair的写端写数据,而读端收到数据。而此时,libevent的内部socket pair读事件已经完成注册。libevent正阻塞在epoll_wait处,当socketp pair读端收到数据时,libevent便从epoll_wait处返回。总之,signal事件通过socket pair,与I/O事件实现完美的统一。

Libevent从epoll_wait返回后,它调用evsignal_process处理信号事件,然后调用event_active将I/O事件(包括内部的socket pair读事件)转移到就绪事件链表。

Socket pair的读事件回调函数:

static void
evsignal_cb(int fd, short what, void *arg)
{    
    static char signals[1];
#ifdef WIN32
    SSIZE_T n;
#else       
    ssize_t n;
#endif       
    //接收数据
    n = recv(fd, signals, sizeof(signals), 0);
    if (n == -1) 
        event_err(1, "%s: read", __func__); 
}      
6、libevent的应用
Libevent是一个非常优秀的开源网络库,它被许多其它开源程序使用。Memcache使用libevent作为底层的网络处理组件,并采用主线程(main thread,单一)+工作线程(work thread,多个)的多线程模型(这将在Memcached的分析中详细介绍)。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值