导语:
对libevent源码分析的文章已经很多,在此不在重述原理,但从另外一个角度讲,知晓原理就是为了更好的使用libevent,本文通过介绍libevent中的mini-heap,socketpair以及对evbuffer的使用,展示libevent所依赖的几个基本数据结构
1. min-heap的使用
理解小根堆的原理和使用,对超时事件的理解比较有帮助,min-heap俗称小根堆,就是一个完全二叉树,根结点的数据比左节点大,比右节点也大,类似
在libevent中作为对超时事件的时间进行排序,根结点的时间最先超时这么个原理
- min-heap根结点的初始化:min_heap_ctor
- 删除根节点: min_heap_dtor
- min-heap的插入: min_heap_push
- min-heap的删除: min_heap_pop
示例如下:
min_heap_ctor(&heap);
for(i = 0;i < 9; ++i) {
inserted[i] = mm_malloc(sizeof(struct event));
set_random_timeout(inserted[i]);
min_heap_push(&heap,inserted[i]);
}
min_heap_print(&heap);
printf("heap size :%d\n",min_heap_size(&heap));
while(1) {
e = min_heap_pop(&heap);
if(!e)
break;
mm_free(e);
}
min_heap_dtor(&heap);
2. socketpair的使用
socketpair的使用场景,主要是在进程间的通信,或者线程间的通信,其原理在源码分析中已经分析的相当透彻,在libevent中的使用,主要是用来在子线程中有可读事件,可写事件,通知主线程,在使用场景中的其他用途,你完全可以发挥想象,socketpair实际上就是两个本地的套接字,在一个进程中监听读,在另外一个进程中写
- 创建socketpair的方法: evutil_socketpair
示例如下:
ret = evutil_socketpair(LOCAL_SOCKETPAIR_AF,SOCK_STREAM,0,fds);
if( ret < 0 ) {
printf("evutil_socketpair falt %d!",ret);
return -1;
}
if( fork() ) {
//parent process
int count = 0;
int val;
close(fds[1]);
while(1){
++count;
write(fds[0],&count,sizeof count);
read(fds[0],&val,sizeof val);
printf("recv data :%d\n",val);
}
}else{
//child process
int val;
close(fds[0]);
while(1){
sleep(1);
read(fds[1],&val,sizeof(val));
printf("recv data :%d\n",val);
val = -val;
write(fds[1],&val,sizeof val);
}
}
3. evbuffer的使用
evbuffer的作用顾名思义就是读写的缓冲区,这部分实现的好坏,实际上关乎一个网络组件的效率高低,所以evbuffer的实现,还是需要花时间去理解,不过使用起来还是蛮简单的,大部分的接口,可以参考event2/buffer.h,这里需要注意的是,当evbuffer在多线程环境下使用时首先要显式调用evthread_use_pthreads函数,并且要调用 evbuffer_enable_locking来初始化evbuffer中的锁,另外需要注意evbuffer是用链表形式实现的,一个节点写入数据会有空闲块,这个空闲块小于一定的阈值时,将创建写节点来写入数据,当节点数据很小时,会创建一个新节点,来容纳这个节点数据和新写入的数据
- evbuffer_new 创建evbuffer
- evbuffer_add 往evbuffer中添加数据
- evbuffer_remove 从evbuffer中拷贝数据并删除
- evbuffer_drain 直接删除数据
示例代码:
//写进程
while(1) {
sprintf(message,message_s,++count);
message_len = strlen(message);
//写入数据 \n结尾
evbuffer_add(buf,message,message_len);
length = evbuffer_get_length(buf);
printf("thread write :%d\n",length);
total += message_len;
sleep(1);
}
//读进程
read_message = evbuffer_readln(buf,&read_message_len,EVBUFFER_EOL_CRLF);
if (NULL != read_message) {
printf("thread read :%s\n",read_message);
length += read_message_len;
}
完整参考:https://github.com/dengwenyi88/test_libevent/
结论:读者也可以在示例中使用更多的接口,加深使用libevent的理解,同时对源码实现的基本原理的理解有帮助,bufferevent的工作流程是依赖于,对event和event_base的进一步封装,在对套接字的读超时和写超时处理,就用到事件的超时处理,同时对socket的读监听事件和写监听事件,都涉及到event_base的基本原理,以及evbuffer中事件的回调处理
参考: