libevent源码分析(细节版)1
来源:互联网 发布:手机淘宝怎么退出 编辑:程序博客网 时间:2024/06/05 11:04
从sample/hello-world.c来剖析libevent源码
我准备从libevent源码中最简单的sample开始剖析源码。目前思路是dfs的方法,即碰到一个不够底层的封装,就去深究它,直到彻底搞懂它,然后返回继续。
先上hello-world.c的源码
#include <string.h>#include <errno.h>#include <stdio.h>#include <signal.h>#ifndef _WIN32#include <netinet/in.h># ifdef _XOPEN_SOURCE_EXTENDED# include <arpa/inet.h># endif#include <sys/socket.h>#endif#include <event2/bufferevent.h>#include <event2/buffer.h>#include <event2/listener.h>#include <event2/util.h>#include <event2/event.h>static const char MESSAGE[] = "Hello, World!\n";static const int PORT = 9995;static void listener_cb(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *);static void conn_writecb(struct bufferevent *, void *);static void conn_eventcb(struct bufferevent *, short, void *);static void signal_cb(evutil_socket_t, short, void *);intmain(int argc, char **argv){ struct event_base *base; struct evconnlistener *listener; struct event *signal_event; struct sockaddr_in sin;#ifdef _WIN32 WSADATA wsa_data; WSAStartup(0x0201, &wsa_data);#endif base = event_base_new(); if (!base) { fprintf(stderr, "Could not initialize libevent!\n"); return 1; } memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(PORT); listener = evconnlistener_new_bind(base, listener_cb, (void *)base, LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1, (struct sockaddr*)&sin, sizeof(sin)); if (!listener) { fprintf(stderr, "Could not create a listener!\n"); return 1; } signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base); if (!signal_event || event_add(signal_event, NULL)<0) { fprintf(stderr, "Could not create/add a signal event!\n"); return 1; } event_base_dispatch(base); evconnlistener_free(listener); event_free(signal_event); event_base_free(base); printf("done\n"); return 0;}static voidlistener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *sa, int socklen, void *user_data){ struct event_base *base = user_data; struct bufferevent *bev; bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); if (!bev) { fprintf(stderr, "Error constructing bufferevent!"); event_base_loopbreak(base); return; } bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL); bufferevent_enable(bev, EV_WRITE); bufferevent_disable(bev, EV_READ); bufferevent_write(bev, MESSAGE, strlen(MESSAGE));}static voidconn_writecb(struct bufferevent *bev, void *user_data){ struct evbuffer *output = bufferevent_get_output(bev); if (evbuffer_get_length(output) == 0) { printf("flushed answer\n"); bufferevent_free(bev); }}static voidconn_eventcb(struct bufferevent *bev, short events, void *user_data){ if (events & BEV_EVENT_EOF) { printf("Connection closed.\n"); } else if (events & BEV_EVENT_ERROR) { printf("Got an error on the connection: %s\n", strerror(errno));/*XXX win32*/ } /* None of the other events can happen here, since we haven't enabled * timeouts */ bufferevent_free(bev);}static voidsignal_cb(evutil_socket_t sig, short events, void *user_data){ struct event_base *base = user_data; struct timeval delay = { 2, 0 }; printf("Caught an interrupt signal; exiting cleanly in two seconds.\n"); event_base_loopexit(base, &delay);}
event_base
第一句是一个event_base结构体
来看下这个结构体的定义
struct event_base { const struct eventop *evsel; 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; };
struct event_list
:
这个的定义在event_struct.h中 TAILQ_HEAD (event_list, event); 。其中TAILQ_HEAD是一个FreeBSD提供的一个宏,或者说一种尾队列。所以event_list就是一种队列类型,其中的元素的的类型是event。更详细就是
struct event_list { struct event *tqh_first; struct event **tqh_last;};
提示:libevent作者此处是用了FREEBSD提供的队列,具体实现在/usr/include/sys/queue.h里面,但是作者copy了一份到libevent/compat/sys/queue.h里面
从网上可以搜搜这个队列的实现,几十年前的上古C代码,大概就是怎么用C实现C++的模板类。
void *evbase
:
evconnlistener_new_bind
这个函数实际包括event_new()和event_add(),先说event_add(struct event *ev,const struct timeval *tv)
intevent_add(struct event *ev, const struct timeval *tv){ int res; if (EVUTIL_FAILURE_CHECK(!ev->ev_base)) { event_warnx("%s: event has no event_base set.", __func__); return -1; } EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock); res = event_add_nolock_(ev, tv, 0); EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock); return (res);}
EVUTIL_FAILURE_CHECK(!ev->ev_base) 这个debug用的宏,不用管
EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock);展开后是 EVLOCK(ev->ev_base->th_base_lock,0);再展开是
if(ev->ev_base->th_base_lock) evthread_lock_fns_.lock(0,ev->ev_base->th_base_lock)
其中evthread_lock_fns_是一个全局变量,在evthread.c里面声明的,其类型是
struct evthread_lock_callbacks { /** The current version of the locking API. Set this to * EVTHREAD_LOCK_API_VERSION */ int lock_api_version; /** Which kinds of locks does this version of the locking API * support? A bitfield of EVTHREAD_LOCKTYPE_RECURSIVE and * EVTHREAD_LOCKTYPE_READWRITE. * * (Note that RECURSIVE locks are currently mandatory, and * READWRITE locks are not currently used.) **/ unsigned supported_locktypes; /** Function to allocate and initialize new lock of type 'locktype'. * Returns NULL on failure. */ void *(*alloc)(unsigned locktype); /** Funtion to release all storage held in 'lock', which was created * with type 'locktype'. */ void (*free)(void *lock, unsigned locktype); /** Acquire an already-allocated lock at 'lock' with mode 'mode'. * Returns 0 on success, and nonzero on failure. */ int (*lock)(unsigned mode, void *lock); /** Release a lock at 'lock' using mode 'mode'. Returns 0 on success, * and nonzero on failure. */ int (*unlock)(unsigned mode, void *lock);};
好吧,其实发现深究到这,这就是一个坑,先跳过这个锁吧,下次再来看。这个地方应该就是上锁解锁,保持队列的线程安全。主要还是看res = event_add_nolock_(ev, tv, 0);这个函数的源码在event.c里面,但是我这个版本的这个函数的实现,已经不适合阅读了。这里只简单的注释一下(因为我也没完全看懂)
intevent_add_nolock_(struct event *ev, const struct timeval *tv, int tv_is_absolute){ struct event_base *base = ev->ev_base; int res = 0; int notify = 0; EVENT_BASE_ASSERT_LOCKED(base); event_debug_assert_is_setup_(ev); event_debug(( "event_add: event: %p (fd "EV_SOCK_FMT"), %s%s%s%scall %p", ev, EV_SOCK_ARG(ev->ev_fd), ev->ev_events & EV_READ ? "EV_READ " : " ", ev->ev_events & EV_WRITE ? "EV_WRITE " : " ", ev->ev_events & EV_CLOSED ? "EV_CLOSED " : " ", tv ? "EV_TIMEOUT " : " ", ev->ev_callback)); EVUTIL_ASSERT(!(ev->ev_flags & ~EVLIST_ALL)); if (ev->ev_flags & EVLIST_FINALIZING) { /* XXXX debug */ return (-1); } /* * prepare for timeout insertion further below, if we get a * failure on any step, we should not change any state. */ //如果添加的新事件是定时器,则为其在时间堆上预留一个位子 if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) { if (min_heap_reserve_(&base->timeheap, 1 + min_heap_size_(&base->timeheap)) == -1) return (-1); /* ENOMEM == errno */ } /* If the main thread is currently executing a signal event's * callback, and we are not the main thread, then we want to wait * until the callback is done before we mess with the event, or else * we can race on ev_ncalls and ev_pncalls below. */#ifndef EVENT__DISABLE_THREAD_SUPPORT if (base->current_event == event_to_event_callback(ev) && (ev->ev_events & EV_SIGNAL) && !EVBASE_IN_THREAD(base)) { ++base->current_event_waiters; EVTHREAD_COND_WAIT(base->current_event_cond, base->th_base_lock); }#endif//这个判断条件大致意思是 如果是读写已经信号事件并且这个事件没有入队过 if ((ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED|EV_SIGNAL)) && !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE|EVLIST_ACTIVE_LATER))) { //此处是将文件描述符或者信号之类的句柄统一化,统一映射为一种int型的数据,这个映射函数接下来会解析 if (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED)) res = evmap_io_add_(base, ev->ev_fd, ev); else if (ev->ev_events & EV_SIGNAL) res = evmap_signal_add_(base, (int)ev->ev_fd, ev); if (res != -1) event_queue_insert_inserted(base, ev);//将新事件加入队列 if (res == 1) { /* evmap says we need to notify the main thread. */ notify = 1; res = 0; } } /* * we should change the timeout state only if the previous event * addition succeeded. */ if (res != -1 && tv != NULL) { struct timeval now; int common_timeout;#ifdef USE_REINSERT_TIMEOUT int was_common; int old_timeout_idx;#endif /* * for persistent timeout events, we remember the * timeout value and re-add the event. * * If tv_is_absolute, this was already set. */ if (ev->ev_closure == EV_CLOSURE_EVENT_PERSIST && !tv_is_absolute) ev->ev_io_timeout = *tv;#ifndef USE_REINSERT_TIMEOUT if (ev->ev_flags & EVLIST_TIMEOUT) { event_queue_remove_timeout(base, ev); }//以上是对定时事件的处理#endif //读到此处其实我有一个地方一直没搞懂,就是所谓的三个事件队列到底对应什么,i/o,signal,active队列。其中有EVLIST_INSERTED,EVLIST_ACTIVE. //事件何时进入inserted队列,何时进入active队列,什么是active状态,这个状态说明什么,在何时到达这个状态??????? /* Check if it is active due to a timeout. Rescheduling * this timeout before the callback can be executed * removes it from the active list. */ if ((ev->ev_flags & EVLIST_ACTIVE) && (ev->ev_res & EV_TIMEOUT)) { if (ev->ev_events & EV_SIGNAL) { /* See if we are just active executing * this event in a loop */ if (ev->ev_ncalls && ev->ev_pncalls) { /* Abort loop */ *ev->ev_pncalls = 0; } } event_queue_remove_active(base, event_to_event_callback(ev)); } gettime(base, &now); common_timeout = is_common_timeout(tv, base);#ifdef USE_REINSERT_TIMEOUT was_common = is_common_timeout(&ev->ev_timeout, base); old_timeout_idx = COMMON_TIMEOUT_IDX(&ev->ev_timeout);#endif if (tv_is_absolute) {//根据定时器类型,判断加入通用定时器队列还是插入时间堆 ev->ev_timeout = *tv; } else if (common_timeout) { struct timeval tmp = *tv; tmp.tv_usec &= MICROSECONDS_MASK; evutil_timeradd(&now, &tmp, &ev->ev_timeout); ev->ev_timeout.tv_usec |= (tv->tv_usec & ~MICROSECONDS_MASK); } else { evutil_timeradd(&now, tv, &ev->ev_timeout); } event_debug(( "event_add: event %p, timeout in %d seconds %d useconds, call %p", ev, (int)tv->tv_sec, (int)tv->tv_usec, ev->ev_callback));#ifdef USE_REINSERT_TIMEOUT event_queue_reinsert_timeout(base, ev, was_common, common_timeout, old_timeout_idx);#else event_queue_insert_timeout(base, ev);#endif if (common_timeout) { struct common_timeout_list *ctl = get_common_timeout_list(base, &ev->ev_timeout); if (ev == TAILQ_FIRST(&ctl->events)) { common_timeout_schedule(ctl, &now, ev); } } else { struct event* top = NULL; /* See if the earliest timeout is now earlier than it * was before: if so, we will need to tell the main * thread to wake up earlier than it would otherwise. * We double check the timeout of the top element to * handle time distortions due to system suspension. */ if (min_heap_elt_is_top_(ev)) notify = 1; else if ((top = min_heap_top_(&base->timeheap)) != NULL && evutil_timercmp(&top->ev_timeout, &now, <)) notify = 1; } } /* if we are not in the right thread, we need to wake up the loop */ if (res != -1 && notify && EVBASE_NEED_NOTIFY(base)) evthread_notify_base(base); event_debug_note_add_(ev); return (res);}
以上event_add_nolock_主要有三个函数,
evmap_io_add 该函数将IO事件添加到多路分发器中,并将对应的事件处理器添加到IO队列中,同时建立IO事件和IO事件处理器的映射关系
evmap_signal_add 同上
event_queue_insert 该函数将事件处理器添加到各种事件队列中
(《Linux高性能服务器编程》游双 著)
先写到这里吧,马上开学了,回学校了继续研究。
2017/8/25
- libevent源码分析(细节版)1
- libevent源码分析(1)
- Libevent 源码分析(原创)
- Libevent源码分析(四)--- libevent事件机制
- 【libevent】源码分析(3)-Libevent工作流程探究
- Libevent源码分析 (1) hello-world
- libevent源码分析(2)--初始化
- libevent源码分析--epoll_add()函数
- Libevent源码分析(零)--- 前言
- Libevent源码分析(一)--- 基本数据结构
- Libevent源码分析(二)--- 时间管理
- Libevent源码分析(六)--- bufferevent
- Libevent源码分析(七)--- IOCP
- libevent源码分析
- libevent源码分析
- libevent源码分析
- libevent源码分析
- libevent源码分析
- AbstractXlsxView、AbstractExcelView、AbstractXlsxStreamingView导出excel,文件名中文问题解决
- mysql安装教程
- Linux学习总结(六)
- 注册界面
- WinDbg调试:启动和退出
- libevent源码分析(细节版)1
- 开发一个简易音乐播放器
- java入门基础之数据类型 个人笔记
- 登录界面
- linux简单操作和自检
- 实习小结(一)
- CodeForces
- spring MVC原理
- Codeforces 811E Vladik and Entertaining Flags [线段树][并查集]