/*==============================================================================
* 2019 -06-30 写在最前面
* libevent 简介不多说
* 安装:
* -->libevent.org官网下载,
* 我用的最新的2.1.10稳定版和之前的1.4.x版本还是有些区别的,
* 比如事件的内部结构体定义,循环处理,小根堆,线程安全等,
* 感觉新版本对以前的功能和安全做了完善
* -->解压tar -xvzf libevent.tar.gz
* -->进入根目录,执行./autogen.sh生成configure (可能需要安装一些软件比如aclocal等因为autogen需要)
* -->执行./configure --prefix=/home/share/install
* -->执行make verify
* -->执行make && make install
*==============================================================================
* 2018-10-25
* 源码目录文件说明:
* event.h - 事件宏定义接口函数声明,主要结构体event的声明
* xxx-internal.h - 内部头文件即libevent内部调用,对外不可见,以达到信息隐藏
* event.c - event整体框架代码
* epoll.c - epoll封装
* select.c - select封装
* devpoll.c - dev/poll封装
* kqueue.c - kqueue封装
* min-heap.h - 以时间作为key的小根堆结构
* signal.c - 信号处理
* evutil.h和evutil.c - 辅助功能函数,包括创建socket pair和一些事件操作函数
* log.h和log.c - 日志函数
* evbuffer.c和buffer.c - libevent对缓冲区的封装
* compat/sys下queue.h - 基本数据结构实现,包括链表,双向链表,队列等
* http和evdns - 是基于libevent实现的http服务器和异步dns查询库
*
*==============================================================================
* 事件event结构体说明://所在头文件编译安装后的根目录event.h
* struct event {
* struct event_callback ev_evcallback; //
*
* union {
* TAILQ_ENTRY(event) ev_next_with_common_timeout;
* int min_heap_idx;
* } ev_timeout_pos;
* evutil_socket_t ev_fd; //绑定的文件描述符
*
* struct event_base *ev_base; //事件绑定的实例
*
* union {
*
* struct {
* LIST_ENTRY (event) ev_io_next;
* struct timeval ev_timeout;
* } ev_io;
*
* struct {
* LIST_ENTRY (event) ev_signal_next;
* short ev_ncalls;
*
* short *ev_pncalls;
* } ev_signal;
* } ev_;
*
* //关注的事件类型EV_WRITE和EV_READ - I/O事件; EV_TIMEOUT - 定时事件; EV_SIGNAL - 信号; EV_PERSIST - 辅助选项
* //#define EV_TIMEOUT 0x01
* //#define EV_READ 0x02
* //#define EV_WRITE 0x04
* //#define EV_SIGNAL 0x08
* //#define EV_PERSIST 0x10
* short ev_events;
* short ev_res; //记录当前激活事件的类型
* struct timeval ev_timeout;
* };
* ==============================================================================
* 2018-11-15
* 事件处理框架 event_base //所在头文件libevent的源码根目录event-internal.h
* 该结构包含了三种事件:I/O事件、信号事件、定时事件,其中前两种分别存储在对应的链表队列中
* 定时事件放在小根堆上(比较酷的处理)
* struct event_base {
* //Function pointers and other data to describe this event_base's backend.
*
* //定义了事件后端的操作比如后端的
* //*name,
* //(*init)(初始化),
* //(*add)(注册事件),
* //(*del)(删除事件),
* //(*dispatch)(事件分发),
* //(*dealloc)(注销释放资源)
* //,等 ,该结构体的函数实现会依赖epoll,poll,select等
* const struct eventop *evsel;
* // Pointer to backend-specific data.
* void *evbase; //定义了后端的数据比如添加一个事件evsel->add(evbase, ev);
*
* // List of changes to tell backend about at next dispatch. Only used by the O(1) backends.
* struct event_changelist changelist;
*
* //Function pointers used to describe the backend that this event_base uses for signals
* const struct eventop *evsigsel; //管理信号的操作
* // Data to implement the common signal handelr code.
* struct evsig_info sig; //信号实例
*
* // Number of virtual events
* int virtual_event_count;
* // Maximum number of virtual events active
* int virtual_event_count_max;
* // Number of total events added to this event_base
* int event_count;
* // Maximum number of total events added to this event_base
* int event_count_max;
* // Number of total events active in this event_base
* int event_count_active;
* // Maximum number of total events active in this event_base
* int event_count_active_max;
*
* // Set if we should terminate the loop once we're done processing
* * events.
* int event_gotterm; //完成事件后可以设置该值去终止循环
* // Set if we should terminate the loop immediately
* int event_break; //设置该值可以随时终止循环
* // Set if we should start a new instance of the loop immediately.
* int event_continue; //启动一个新的循环
*
* // The currently running priority of events
* int event_running_priority; //当前运行的事件优先级
*
* // Set if we're running the event_base_loop function, to prevent
* * reentrant invocation.
* int running_loop; //防止循环重入
*
* // Set to the number of deferred_cbs we've made 'active' in the
* * loop. This is a hack to prevent starvation; it would be smarter
* * to just use event_config_set_max_dispatch_interval's max_callbacks
* * feature
* int n_deferreds_queued;
*
* /* Active event management.
* // An array of nactivequeues queues for active event_callbacks (ones
* * that have triggered, and whose callbacks need to be called). Low
* * priority numbers are more important, and stall higher ones.
*
* struct evcallback_list *activequeues; //可理解成存放队列的数组,数组的每个节点存放着一个队列,数字越低优先级越高,该变量理解为数组,优先级标号为数组下标
* // The length of the activequeues array
* int nactivequeues; //链表长度
* // A list of event_callbacks that should become active the next time
* // * we process events, but not this time.
* struct evcallback_list active_later_queue; //下次激活的事件链表,下次会处理,但本次不会
*
* /* common timeout logic
*
* // An array of common_timeout_list* for all of the common timeout
* * values we know.
* struct common_timeout_list **common_timeout_queues;
* // The number of entries used in common_timeout_queues
* int n_common_timeouts;
* // The total size of common_timeout_queues.
* int n_common_timeouts_allocated;
*
* // Mapping from file descriptors to enabled (added) events
* struct event_io_map io; //注册的事件文件描述符的映射
*
* // Mapping from signal numbers to enabled (added) events.
* struct event_signal_map sigmap;
*
* // Priority queue of events with timeouts.
* struct min_heap timeheap; //管理定时事件的小根堆,优先处理小根堆的顶节点
*
* // Stored timeval: used to avoid calling gettimeofday/clock_gettime
* * too often.
* struct timeval tv_cache; //时间管理
*
* struct evutil_monotonic_timer monotonic_timer;
*
* // Difference between internal time (maybe from clock_gettime) and
* * gettimeofday.
* struct timeval tv_clock_diff;
* // Second in which we last updated tv_clock_diff, in monotonic time.
* time_t last_updated_clock_diff;
*
* #ifndef EVENT__DISABLE_THREAD_SUPPORT
* /* threading support
* // The thread currently running the event_loop for this base
* unsigned long th_owner_id;
* // A lock to prevent conflicting accesses to this event_base
* void *th_base_lock;
* // A condition that gets signalled when we're done processing an
* * event with waiters on it.
* void *current_event_cond;
* // Number of threads blocking on current_event_cond.
* int current_event_waiters;
* #endif
* // The event whose callback is executing right now
* struct event_callback *current_event;
*
* #ifdef _WIN32
* // IOCP support structure, if IOCP is enabled.
* struct event_iocp_port *iocp;
* #endif
*
* // Flags that this base was configured with
* enum event_base_config_flag flags;
*
* struct timeval max_dispatch_time;
* int max_dispatch_callbacks;
* int limit_callbacks_after_prio;
*
* /* Notify main thread to wake up break, etc.
* // True if the base already has a pending notify, and we don't need
* * to add any more.
* int is_notify_pending;
* // A socketpair used by some th_notify functions to wake up the main
* * thread.
* evutil_socket_t th_notify_fd[2];
* // An event used by some th_notify functions to wake up the main
* * thread.
* struct event th_notify;
* // A function used to wake up the main thread from another thread.
* int (*th_notify_fn)(struct event_base *base);
*
* // Saved seed for weak random number generator. Some backends use
* * this to produce fairness among sockets. Protected by th_base_lock.
* struct evutil_weakrand_state weakrand_seed;
*
* // List of event_onces that have not yet fired.
* LIST_HEAD(once_event_list, event_once) once_events;
*
* };
* ==============================================================================
* 2019-03-15
* event接口函数: event_add、event_del、event_base_loop
* //在源码根目录下的event.c中有实现
* int event_add(struct event *ev, const struct timeval *timeout) //注册事件
* int
* event_add(struct event *ev, const struct timeval *tv)
* {
* int res;
* //#define EVUTIL_FAILURE_CHECK(cond) 0 该宏原型如此,没看出玄机!!!
* if (EVUTIL_FAILURE_CHECK(!ev->ev_base)) {
* event_warnx("%s: event has no event_base set.", __func__);
* return -1;
* }
* //神奇的do while(0) ,参考我的博文有对其讲解
* //#define EVBASE_ACQUIRE_LOCK(base, lockvar) do { \
* // EVLOCK_LOCK((base)->lockvar, 0); \
* // } while (0)
* EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock);
*
* //该函数有两个特殊的地方:1,需要我们有锁,2,第三个参数tv_is_absolute被设置了,则这个时间是一个绝对时间,独立的,而不是添加到现有的时间间隔上
* //内部实现上将新事件在堆上预留
* //插入注册链表(内部实现上1,检测事件是否已在激活链表,2,对三类不同的事件分别处理,I/O或signal加入注册事件链表,就绪事件加入激活链表,定时事件加入小根堆)
* //添加定时事件
* //更新小根堆
* res = event_add_nolock_(ev, tv, 0);
*
* EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock);
*
* return (res);
* }
*
* 删除事件
* static int
* event_del_(struct event *ev, int blocking)
* {
* 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);
* //blocking必须是 EVENT_DEL_{BLOCK, NOBLOCK, AUTOBLOCK,EVEN_IF_FINALIZING} 中的一个
* //先检测blocking
* //检测ev是否注册
//取出ev_base,
//从对应的链表队列中删除
* res = event_del_nolock_(ev, blocking);
*
* EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock);
*
* return (res);
* }
*
* int
* event_del(struct event *ev)
* {
* return event_del_(ev, EVENT_DEL_AUTOBLOCK);
* }
*
* 事件循环
* int
* event_loop(int flags)
* {
* return event_base_loop(current_base, flags);
* }
*
主循环中执行流程:
->检测是否有跳出条件
->根据所有timer事件的最小超时时间来设置系统I/O的timeout,返回后激活所有就绪的timer事件
->检测是否有激活事件,存在就poll一个事件
->更新激活等待时间
->将就绪的I/O插入激活链表
->将激活的signal对应的event插入激活链表
->将小根堆的就绪定时事件插入激活链表
->根据优先级处理激活链表中的事件
* int
* event_base_loop(struct event_base *base, int flags) 核心函数
* {
* const struct eventop *evsel = base->evsel;
* struct timeval tv;
* struct timeval *tv_p;
* int res, done, retval = 0;
*
* // Grab the lock. We will release it inside evsel.dispatch, and again
* * as we invoke user callbacks.
* EVBASE_ACQUIRE_LOCK(base, th_base_lock);
*
* if (base->running_loop) {
* event_warnx("%s: reentrant invocation. Only one event_base_loop"
* " can run on each event_base at once.", __func__);
* EVBASE_RELEASE_LOCK(base, th_base_lock);
* return -1;
* }
*
* base->running_loop = 1;
*
* clear_time_cache(base);
*
* if (base->sig.ev_signal_added && base->sig.ev_n_signals_added)
* evsig_set_base_(base);
*
* done = 0;
*
* #ifndef EVENT__DISABLE_THREAD_SUPPORT
* base->th_owner_id = EVTHREAD_GET_ID();
* #endif
*
* base->event_gotterm = base->event_break = 0;
*
* while (!done) {
* base->event_continue = 0;
* base->n_deferreds_queued = 0;
* //外部是否存在跳出循环的条件
* // Terminate the loop if we have been asked to
* if (base->event_gotterm) {
* //可通过event_loopexit_cb()设置event_gotterm
* break;
* }
*
* if (base->event_break) {
* //可通过event_base_loopbreak()设置event_break
* break;
* }
* //取小根堆的最小超时时间
* tv_p = &tv;
* //该事件是否激活,有激活事件就poll一个事件出来并处理无需等待
* if (!N_ACTIVE_CALLBACKS(base) && !(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 (0==(flags&EVLOOP_NO_EXIT_ON_EMPTY) &&
* !event_haveevents(base) && !N_ACTIVE_CALLBACKS(base)) {
* event_debug(("%s: no events registered.", __func__));
* retval = 1;
* goto done;
* }
* //更新等待激活的时间
* event_queue_make_later_events_active(base);
* //清空时间缓存
* clear_time_cache(base);
* //将就绪的事件、I/O事件插入激活链表
* res = evsel->dispatch(base, tv_p);
*
* if (res == -1) {
* event_debug(("%s: dispatch returned unsuccessfully.",
* __func__));
* retval = -1;
* goto done;
* }
* //更新事件缓存
* update_time_cache(base);
* //检测小根堆中的事件是否就绪,将就绪事件插入激活链表并从小根堆中删除
* timeout_process(base);
* //将激活链表中的就绪事件调用其回调函数进行处理,处理规则按优先级进行,存在低优先级的得不到及时处理
* if (N_ACTIVE_CALLBACKS(base)) {
* int n = event_process_active(base);
* if ((flags & EVLOOP_ONCE)
* && N_ACTIVE_CALLBACKS(base) == 0
* && n != 0)
* done = 1;
* } else if (flags & EVLOOP_NONBLOCK)
* done = 1;
* }
* event_debug(("%s: asked to terminate loop.", __func__));
* done:
* clear_time_cache(base);
* base->running_loop = 0;
*
* EVBASE_RELEASE_LOCK(base, th_base_lock);
*
* return (retval);
* }
* ==============================================================================
* 2019-07-19
* 程序说明: 该程序定义了两个base:一个是用于socket,一个用于定时事件,socket和定时事件没区别开;
* 该程序只是用于测试,里面存在fd泄露问题未处理
* 2019-07-17 编写socket的读写事件和client进行测试[done]
* 2019-07-18 编写定时事件并测试[done]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/signal.h>
#include <sys/resource.h>
#include <pthread.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sched.h>
#include <signal.h>
#include <sys/time.h>
#include "event2/event.h"
#include "event2/bufferevent.h"
/*
struct event;
*/
#include "event2/event_struct.h"
/*
定义#define evtimer_set(ev, cb, arg) event_set((ev), -1, 0, (cb), (arg))
*/
#include "event2/event_compat.h"
static void printLog(int severity,const char *msg)
{
printf("level[%d]:%s\n",severity,msg);
}
static void printLog1(int severity,const char *msg, int ret)
{
printf("level[%d]:%s,0x%x.\n",severity,msg,ret);
}
//typedef void (*event_fatal_cb)(int err);
static void errProc(int err)
{
printf("err[%d].\n",err);
}
void do_read(evutil_socket_t fd, short what, void *args)
{
unsigned char buff[1024];
int len = 0;
memset(buff,'\0',1024);
len = recv(fd,buff,1024,0);
if(len <= 0)
{
event_msgx("read nothing.");
return ;
}
event_msgx("read[%d]:%s",len,buff);
}
void do_write(evutil_socket_t fd, short what, void *args)
{
int len = 0;
len = send(fd,args,strlen(args),0);
if(len <= 0)
{
event_msgx("write err.");
return ;
}
event_msgx("write[%d]:%s",len,args);
}
void event_work(struct event_base *base,
evutil_socket_t fd,
short what)
{
int ret;
//分配和构造一个新事件
struct event *ev;
switch(what){
case EV_READ:
//printf("ev_read.\n");
ev = event_new(base,fd,what,do_read,"read event.");
if(ev == NULL)
{
printf("event new err.\n");
return ;
}
break;
case EV_WRITE:
ev = event_new(base,fd,what,do_write,"write event.");
break;
}
const struct timeval tv = {5,0};
//printf("event add.\n");
ret = event_add(ev,&tv);
if(ret < 0)
{
printf("event add err.\n");
return ;
}
//printf("event dispatch.\n");
//事件分发,注意最开始用的event_dispatch总是段错误用错了
ret = event_base_dispatch(base);
//printf("register done.\n");
}
void event_base_config()
{
int i = 0;
struct event_config *cfg;
struct event_base *base;
cfg = event_config_new();
//禁用epoll
event_config_avoid_method(cfg,"epoll");
base = event_base_new_with_config(cfg);
//获取支持的方法
const char **methods = event_get_supported_methods();
printf("config event base support methods:\n");
for(i = 0;methods[i] != NULL;i++)
{
printf("[%d]\t%s\n",i+1,methods[i]);
}
event_config_free(cfg);
//获取当前用的方法,禁用epoll后该出使用poll
char *mechanism = (char *)event_base_get_method(base);
printf("used is %s\n",mechanism);
event_base_free(base);
}
void srvInit(struct event_base *base)
{
int flag;
int pos;
int clifd, client_len;
struct sockaddr_in server_address, client_address;
struct linger timeout;
int fdsock_num;
int ret =0;
//构造一个server
int srvfd = socket(AF_INET,SOCK_STREAM,0);
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = inet_addr("127.0.0.1");
server_address.sin_port = htons(9001);
if (bind(srvfd, (struct sockaddr*)&server_address, sizeof(server_address)) < 0)
{
event_err("bind err.\n");
return ;
}
if ((listen (srvfd, SOMAXCONN)) < 0)
{
event_err("listen err.\n");
return ;
}
printf("===================================\n");
printf("start recv:\n");
while(1)
{
clifd = accept (srvfd, (struct sockaddr *)&client_address, (socklen_t *)&client_len);
printf("clifd[%d] ok.\n",clifd);
//该处可配合线程池做高并发,下面是一个错误的示范,但仍可运行是为了调试读写事件
event_work(base,clifd,EV_READ);
event_work(base,clifd,EV_WRITE);
}
}
//设置定时器
struct event ev_timer;
static void taskTimer(evutil_socket_t fd, short event, void *args)
{
struct timeval tv = {1,0};
//struct event_base *base = (struct event_base *)args;
printf("time arrive.\n");
event_add(&ev_timer,&tv);
}
int setTimer(struct event_base *base)
{
struct timeval tv = {1,0};
evtimer_set(&ev_timer,taskTimer,&ev_timer);
event_base_set(base,&ev_timer);
//evtimer_add(&ev_timer,&tv);
event_add(&ev_timer,&tv);
//事件分发,注意最开始用的event_dispatch总是段错误用错了
event_base_dispatch(base);
}
int main()
{
//自定义函数要写在开头,后面base在申请时会调用
//自定义函数顺序:内存-->日志-->异常
//注册异常时执行的函数
event_set_fatal_callback(errProc);
//注册新的日志打印函数
//event_set_log_callback(printLog1);
event_set_log_callback(printLog);
//event_set_log_callback(NULL);
event_msgx("test msgx log oper!");
struct event_base *base = event_base_new();
struct event_base *base_timer = event_base_new();
//获取当前使用的方法
char *mechanism = (char *)event_base_get_method(base);
int i = 0;
//获取当前支持的方法
const char **methods = event_get_supported_methods();
printf("support methods:\n");
for(i = 0; methods[i] != NULL;i++)
{
printf("[%d]\t%s\n",i+1,methods[i]);
}
event_msgx("test msgx log oper!");
printf("used is %s\n",mechanism);
//调用socket的收发事件
printf("注册epoll.\n");
srvInit(base);
printf("注册定时器.\n");
setTimer(base_timer);
event_base_free(base);
//构建复杂的event_base
event_base_config();
return 0;
}
//用于测试server的client程序,比较简单,没做什么错误,异常等的处理
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/signal.h>
#include <sys/resource.h>
#include <pthread.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sched.h>
#include <signal.h>
#include <sys/time.h>
int main(int argc,char **argv)
{
int flag;
int pos;
int clifd, client_len;
struct sockaddr_in server_address, client_address;
struct linger timeout;
int fdsock_num;
int ret =0;
unsigned char buff[100];
//构造一个cli
int srvfd = socket(AF_INET,SOCK_STREAM,0);
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = inet_addr("127.0.0.1");
server_address.sin_port = htons(9001);
if(connect(srvfd,(struct sockaddr *)&server_address,sizeof(struct sockaddr_in))<0)
{
printf("connect err.\n");
return -2;
}
printf("%s\n",argv[1]);
send(srvfd,argv[1],strlen(argv[1]),0);
memset(buff,'\0',100);
recv(srvfd,buff,100,0);
printf("recv:%s.\n",buff);
}
编译和执行
gcc server.c -I ../include/ -L ../lib/ -o server -levent
LD_LIBRARY_PATH=../lib/ ./server
开另一个终端:./client hello