经过了一个月对libevent1.4.13源码的学习,并且自己仿照了,码了一个c++版的,现在分享一下自己对源码关于epoll的一些分析。
学习源码的过程并挺好玩的,给想学习的同学以下建议:
1.用source insight来看代码,非常方便,各种跳转功能和高亮,多窗口。
2.可以看看张亮的分析,没有说对源码全部一句一句分析,其实也没必要,他讲得比较全面,剩下的代码自己完全可以看懂,我就是看他的学习的~
不过在那之前我们先解释下event/event_base/epollop/evsignal_info的分析:
struct event {
TAILQ_ENTRY (event) ev_next; //读写和信号事件在事件链表的位置
TAILQ_ENTRY (event) ev_active_next; //就绪事件在就绪链表的位置
TAILQ_ENTRY (event) ev_signal_next; //信号事件在信号链表的位置
unsigned int min_heap_idx; /* for managing timeouts *///定时时间在小根堆的位置
struct event_base *ev_base; //每个事件关联的事件框架
int ev_fd; //读写事件为文件描述符,信号事件为信号值,定时事件设置为-1
short ev_events; //事件的类型
short ev_ncalls; //回调函数要执行的次数
short *ev_pncalls; /* Allows deletes in callback *///一般指向上面那个变量,用来中断回调函数的执行用
struct timeval ev_timeout; //定时事件的定时结束时间
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; //当前所在的链表类型
};
struct event_base {
const struct eventop *evsel; //统一事件的机制,比如在linux下它就会选择epoll机制
void *evbase; //如果上面选择了epoll机制,那么这个其实就是epoll.c里面的epollop类
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; //用来管理信号事件用的,有一系列操作在signal.c
struct event_list eventqueue; //注册后信号事件或者读写事件所在的事件链表
struct timeval event_tv; //用作时间修正
struct min_heap timeheap; //存定时事件的小根堆
struct timeval tv_cache; //保存现在的时间,其实直接用系统函数就可以得到当前时间,但是费时,所以就用这个来缓存下当前时间,就不用一直用系统调用了。
};
struct evepoll {
struct event *evread;
struct event *evwrite;
};
struct epollop {
struct evepoll *fds; //因为我们用的是epoll_wait来统一事件,所以需要一个文件描述符对应一个epoll_event和event,这个链表作用是用来关联epoll_event结构的,关联后用epoll_wait后可得到第二个参数,即epoll_event*链表,后面可以对对应的event做处理,比如插入就绪链表等动作。
int nfds; //最大文件描述符值
struct epoll_event *events; //跟evepoll 关联的事件链表
int nevents; //用epoll_wait得到的第三个参数,即events的大小
int epfd; //epoll_create得到的管理文件描述符
};
struct evsignal_info {
struct event ev_signal; //用一对socket来统一所有的信号的事件,大概是信号来就调用一个函数(evsignal_handler(写)),然后因为注册了一个event读事件(ev_signal),由epoll_wait得到这个事件后,调用回调函数(evsignal_cb),在(evsignal_handler)的过程已经记录了哪个信号触发了,即以下结构记录的
//struct evsignal_info sig; //用来管理信号事件用的,有一系列操作在signal.c
//这个类记录了之后后面的循环会把对应触发的信号的回调函数执行。
int ev_signal_pair[2]; //注册的一对socket
int ev_signal_added; //是否已注册信号
volatile sig_atomic_t evsignal_caught; //是否有信号触发
struct event_list evsigevents[NSIG]; //信号链表,所有注册的信号都在此链表
sig_atomic_t evsigcaught[NSIG]; //对应信号的触发次数
#ifdef HAVE_SIGACTION
struct sigaction **sh_old; //这个其实是用来恢复注册信号的回调函数的,比如你在程序另一边也注册了这个信号的回调函数,但是这边又用libevent注册了,那么在这个框架结束后它会帮你恢复之前的回调函数。
#else
ev_sighandler_t **sh_old;
#endif
int sh_old_max; //能恢复信号的回调函数的最大数量
}
弄清楚各个成员的含义对后面看代码特别重要~下一篇我们讲函数的分析~