libevent 信号事件实现方式
学会使用libevent,才能真正的掌握其是实现原理,我们先从一个简短的测试用例开始:
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <sys/queue.h> 4 #include <unistd.h> 5 #include <sys/time.h> 6 7 #include <signal.h> 8 #include <fcntl.h> 9 #include <stdlib.h> 10 #include <stdio.h> 11 #include <string.h> 12 #include <errno.h> 13 #include <event.h> 14 15 16 int called = 0; 17 static void signall_cb(int fd, short event, void *arg) 18 { 19 struct event *signal = arg; 20 printf("%s: got signal %d\n", __func__, EVENT_SIGNAL(signal)); 21 if (called >= 2) 22 event_del(signal); 23 called++; 24 } 25 int main (int argc, char **argv) 26 { 27 struct event signal_int; 28 /* Initalize the event library */ 29 event_init(); 30 /* Initalize one event */ 31 event_set(&signal_int, SIGINT, EV_SIGNAL|EV_PERSIST, signall_cb, &signal_int); 32 event_add(&signal_int, NULL); 33 event_dispatch(); 34 return (0); 35 }
该代码的大体意思是:添加一个信号中断事件,通过CTRL+C,产生中断信号,再调用中断处理函数。
首先分析的是函数是event_init函数,现帖出其具体实现方式
194 struct event_base * 195 event_base_new(void) 196 { 197 int i; 198 struct event_base *base; 199 200 if ((base = calloc(1, sizeof(struct event_base))) == NULL) 201 event_err(1, "%s: calloc", __func__); 202 203 event_sigcb = NULL; 204 event_gotsig = 0; 205 206 detect_monotonic(); 207 /*如果第一次使用初始化,需要将时间缓存保留,在event_base中保留了一个事件*/ 208 gettime(base, &base->event_tv); 209 /*初始化最小堆,这个堆里存储的是时间,在I/O复用的函数里是使用最小堆中堆顶的值 210 作为等待的时间参数,这里只介绍epoll_wait作为I/O复用的方法,这个值就是函数的最后 211 一个参数,当等待的时间结束,函数返回,也就代表着可能是定时事件被激活 212 这样也将定时事件集合到I/O事件*/ 213 min_heap_ctor(&base->timeheap); 214 /*宏作为初始化的,eventqueue是一个event_list,这里面存储着这个event_base所关注的 215 所有事件*/ 216 TAILQ_INIT(&base->eventqueue); 217 /*这一对套接字是为了将信号事件融合到I/O事件中所特有的,将其中一个套接字添加到被关注的 218 行列中,信号事件是通过这一对套接字上来传递的,信号到达,其中一个套接字上有I/O事件, 219 那么信号到达可以让I/O返回,这样便可将信号事件添加到激活队列中去........*/ 220 base->sig.ev_signal_pair[0] = -1; 221 base->sig.ev_signal_pair[1] = -1; 222 223 base->evbase = NULL; 224 /*寻找合适的I/O复用机制,在这里说明,libevent库使用的I/O机制是在编译的时候确定的 225 其实evbase和某一个复用机制的关系就像类和对象的关系一样,复用机制是evbase的具体实现 226 */ 227 printf("default is selected %d %d %d\n", HAVE_EPOLL, :qHAVE_POLL, HAVE_SELECT); 228 229 /*eventops 是一个全局的结构体,结构体中都是不同内核所支持的几种I/O复用机制*/ 230 for (i = 0; eventops[i] && !base->evbase; i++) { 231 base->evsel = eventops[i];
是时候好好总结下自己走过的路。