struct event_base {
/* evsel和evbase这两个字段你可以把evsel和evbase看作是类和静态函数的关系,
比如添加事件时的调用行为:evsel->add(evbase, ev),实际执行操作的是evbase;
这相当于class::add(instance, ev),instance就是class的一个对象实例。
evsel指向了全局变量static const struct eventop *eventops[]中的一个;
libevent将系统提供的I/O demultiplex机制统一封装成了eventop结构;
因此eventops[]包含了select、poll、kequeue和epoll等其中的若干个全局实例对象。
evbase实际上是一个eventop实例对象;先来看看eventop结构体,
它的成员是一系列的函数指针, 在event-internal.h文件中:
struct eventop {
const char *name;
void *(*init)(struct event_base *); // 初始化
int (*add)(void *, struct event *); // 注册事件
int (*del)(void *, struct event *); // 删除事件
int (*dispatch)(struct event_base *, void *, struct timeval *); // 事件分发
void (*dealloc)(struct event_base *, void *); // 注销,释放资源
int need_reinit;// set if we need to reinitialize the event base
};
也就是说,在libevent中,每种I/O demultiplex机制的实现都必须提供这五个函数接口,
来完成自身的初始化、销毁释放;对事件的注册、注销和分发。
比如对于epoll,libevent实现了5个对应的接口函数,并在初始化时并将eventop的5个函数指针指向这5个函数,
那么程序就可以使用epoll作为I/O demultiplex机制了 */
const struct eventop *evsel;
/** Pointer to backend-specific data. */
void *evbase;
/** 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;
/** Number of total events added to this event_base */
int event_count;
/** Number of total events active in this event_base */
int event_count_active;
/** 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;//是否正在进行事件循环
/* Active event management. */
/** An array of nactivequeues queues for active events (ones that
* have triggered, and whose callbacks need to be called). Low
* priority numbers are more important, and stall higher ones.
*/
struct event_list *activequeues;//活跃事件的链表
/** The length of the activequeues array */
int nactivequeues;//活跃事件的个数
/* 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;
/** List of defered_cb that are active. We run these after the active
* events. */
struct deferred_cb_queue defer_queue;
/** 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;
/** All events that have been enabled (added) in this event_base */
struct event_list eventqueue;
/** Stored timeval; used to detect when time is running backwards. */
struct timeval event_tv;
/** 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;
#if defined(_EVENT_HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
/** 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;
#endif
#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;
/** The event whose callback is executing right now */
struct event *current_event;
/** 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
#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;
/* 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];//用于线程通信的pair
/** 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);
};
(1)libevent的队列结构体:
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
}
//和前面的TAILQ_HEAD不同,这里的结构体并没有name.即没有结构体名。
//所以该结构体只能作为一个匿名结构体。所以,它一般都是另外一个结构体或者共用体的成员
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_INIT(head) do { \
(head)->tqh_first = NULL; \
(head)->tqh_last = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.tqe_next = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_REMOVE(head, elm, field) do { \
if (((elm)->field.tqe_next) != NULL) \
(elm)->field.tqe_next->field.tqe_prev = \
(elm)->field.tqe_prev; \
else \
(head)->tqh_last = (elm)->field.tqe_prev; \
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
} while (0)
案例:
#include
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
}
//和前面的TAILQ_HEAD不同,这里的结构体并没有name.即没有结构体名。
//所以该结构体只能作为一个匿名结构体。所以,它一般都是另外一个结构体
//或者共用体的成员
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_END(head) NULL
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
/* XXX */
#define TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define TAILQ_EMPTY(head) \
(TAILQ_FIRST(head) == TAILQ_END(head))
#define TAILQ_FOREACH(var, head, field) \
for((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head); \
(var) = TAILQ_NEXT(var, field))
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head); \
(var) = TAILQ_PREV(var, headname, field))
#define TAILQ_INIT(head) do { \
(head)->tqh_first = NULL; \
(head)->tqh_last = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
(head)->tqh_first->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(head)->tqh_first = (elm); \
(elm)->field.tqe_prev = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.tqe_next = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
(elm)->field.tqe_next->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(listelm)->field.tqe_next = (elm); \
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
(elm)->field.tqe_next = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_REMOVE(head, elm, field) do { \
if (((elm)->field.tqe_next) != NULL) \
(elm)->field.tqe_next->field.tqe_prev = \
(elm)->field.tqe_prev; \
else \
(head)->tqh_last = (elm)->field.tqe_prev; \
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
} while (0)
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
(elm2)->field.tqe_next->field.tqe_prev = \
&(elm2)->field.tqe_next; \
else \
(head)->tqh_last = &(elm2)->field.tqe_next; \
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
*(elm2)->field.tqe_prev = (elm2); \
} while (0)
//队列中的元素结构体。它有一个值,并且有前向指针和后向指针
//通过前后向指针,把队列中的节点(元素)连接起来
struct queue_entry_t
{
int value;
//从TAILQ_ENTRY的定义可知,它只能是结构体或者共用体的成员变量
TAILQ_ENTRY(queue_entry_t)entry;
};
//定义一个结构体,结构体名为queue_head_t,成员变量类型为queue_entry_t
//就像有头节点的链表那样,这个是队列头。它有两个指针,分别指向队列的头和尾
TAILQ_HEAD(queue_head_t, queue_entry_t);
int main(int argc, char **argv)
{
struct queue_head_t queue_head;
struct queue_entry_t *q, *p, *s, *new_item;
int i;
TAILQ_INIT(&queue_head);
for(i = 0; i < 3; ++i)
{
p = (struct queue_entry_t*)malloc(sizeof(struct queue_entry_t));
p->value = i;
TAILQ_INSERT_TAIL(&queue_head, p, entry);//在队尾插入数据
}
q = (struct queue_entry_t*)malloc(sizeof(struct queue_entry_t));
q->value = 10;
TAILQ_INSERT_HEAD(&queue_head, q, entry);//在队头插入数据
//现在q指向队头元素、p指向队尾元素
s = (struct queue_entry_t*)malloc(sizeof(struct queue_entry_t));
s->value = 20;
//在队头元素q的后面插入元素
TAILQ_INSERT_AFTER(&queue_head, q, s, entry);
s = (struct queue_entry_t*)malloc(sizeof(struct queue_entry_t));
s->value = 30;
//在队尾元素p的前面插入元素
TAILQ_INSERT_BEFORE(p, s, entry);
//现在进行输出
//获取第一个元素
s = TAILQ_FIRST(&queue_head);
printf("the first entry is %d\n", s->value);
//获取下一个元素
s = TAILQ_NEXT(s, entry);
printf("the second entry is %d\n\n", s->value);
//删除第二个元素, 但并没有释放s指向元素的内存
TAILQ_REMOVE(&queue_head, s, entry);
free(s);
new_item = (struct queue_entry_t*)malloc(sizeof(struct queue_entry_t));
new_item->value = 100;
s = TAILQ_FIRST(&queue_head);
//用new_iten替换第一个元素
TAILQ_REPLACE(&queue_head, s, new_item, entry);
printf("now, print again\n");
i = 0;
TAILQ_FOREACH(p, &queue_head, entry)//用foreach遍历所有元素
{
printf("the %dth entry is %d\n", i, p->value);
}
p = TAILQ_LAST(&queue_head, queue_head_t);
printf("last is %d\n", p->value);
p = TAILQ_PREV(p, queue_head_t, entry);
printf("the entry before last is %d\n", p->value);
}
//队列中的元素结构体。它有一个值,并且有前向指针和后向指针
//通过前后向指针,把队列中的节点连接起来
struct queue_entry_t
{
int value;
struct
{
struct queue_entry_t *tqe_next;
struct queue_entry_t **tqe_prev;
}entry;
};
//就像有头节点的链表那样,这个是队列头,它有两个指针,分别指向队列的头和尾
struct queue_head_t
{
struct queue_entry_t *tqh_first;
struct queue_entry_t **tqh_last;
};
int main(int argc, char **argv)
{
struct queue_head_t queue_head;
struct queue_entry_t *q, *p, *s, *new_item;
int i;
//TAILQ_INIT(&queue_head);
do
{
(&queue_head)->tqh_first = 0;
//tqh_last是二级指针,这里指向一级指针
(&queue_head)->tqh_last = &(&queue_head)->tqh_first;
}while(0);
for(i = 0; i < 3; ++i)
{
p = (struct queue_entry_t*)malloc(sizeof(struct queue_entry_t));
p->value = i;
//TAILQ_INSERT_TAIL(&queue_head, p, entry);在队尾插入数据
do
{
(p)->entry.tqe_next = 0;
//tqh_last存储的是最后一个元素(队列节点)tqe_next成员的地址。
//所以tqe_prev指向了tqe_next。
(p)->entry.tqe_prev = (&queue_head)->tqh_last;
//tqh_last存储的是最后一个元素(队列节点)tqe_next成员
//的地址,所以*(&queue_head)->tqh_last修改的是最后一个
//元素的tqe_next成员的值,使得tqe_next指向*p(新的队列
//节点)。
*(&queue_head)->tqh_last = (p);
//队头结构体(queue_head)的tqh_last成员保存新队列节点的
//tqe_next成员的地址值。即让tqh_last指向tqe_next。
(&queue_head)->tqh_last = &(p)->entry.tqe_next;
}while(0);
}
q = (struct queue_entry_t*)malloc(sizeof(struct queue_entry_t));
q->value = 10;
//TAILQ_INSERT_HEAD(&queue_head, q, entry);在队头插入数据
do {
//queue_head队列中已经有节点(元素了)。要对第一个元素进行修改
if(((q)->entry.tqe_next = (&queue_head)->tqh_first) != 0)
(&queue_head)->tqh_first->entry.tqe_prev = &(q)->entry.tqe_next;
else//queue_head队列目前是空的,还没有任何节点(元素)。修改queue_head即可
(&queue_head)->tqh_last = &(q)->entry.tqe_next;
//queue_head的first指针指向要插入的节点*q
(&queue_head)->tqh_first = (q);
(q)->entry.tqe_prev = &(&queue_head)->tqh_first;
}while(0);
//现在q指向队头元素、p指向队尾元素
s = (struct queue_entry_t*)malloc(sizeof(struct queue_entry_t));
s->value = 20;
//TAILQ_INSERT_AFTER(&queue_head, q, s, entry);在队头元素q的后面插入元素
do
{
//q不是最后队列中最后一个节点。要对q后面的元素进行修改
if (((s)->entry.tqe_next = (q)->entry.tqe_next) != 0)
(s)->entry.tqe_next->entry.tqe_prev = &(s)->entry.tqe_next;
else//q是最后一个元素。对queue_head修改即可
(&queue_head)->tqh_last = &(s)->entry.tqe_next;
(q)->entry.tqe_next = (s);
(s)->entry.tqe_prev = &(q)->entry.tqe_next;
}while(0);
s = (struct queue_entry_t*)malloc(sizeof(struct queue_entry_t));
s->value = 30;
//TAILQ_INSERT_BEFORE(p, s, entry); 在队尾元素p的前面插入元素
do
{
//无需判断节点p前面是否还有元素。因为即使没有元素,queue_head的两个
//指针从功能上也相当于一个元素。这点是采用二级指针的一大好处。
(s)->entry.tqe_prev = (p)->entry.tqe_prev;
(s)->entry.tqe_next = (p);
*(p)->entry.tqe_prev = (s);
(p)->entry.tqe_prev = &(s)->entry.tqe_next;
}while(0);
//现在进行输出
// s = TAILQ_FIRST(&queue_head);
s = ((&queue_head)->tqh_first);
printf("the first entry is %d\n", s->value);
// s = TAILQ_NEXT(s, entry);
s = ((s)->entry.tqe_next);
printf("the second entry is %d\n\n", s->value);
//删除第二个元素, 但并没有释放s指向元素的内存
//TAILQ_REMOVE(&queue_head, s, entry);
do
{
if (((s)->entry.tqe_next) != 0)
(s)->entry.tqe_next->entry.tqe_prev = (s)->entry.tqe_prev;
else (&queue_head)->tqh_last = (s)->entry.tqe_prev;
*(s)->entry.tqe_prev = (s)->entry.tqe_next;
}while(0);
free(s);
new_item = (struct queue_entry_t*)malloc(sizeof(struct queue_entry_t));
new_item->value = 100;
//s = TAILQ_FIRST(&queue_head);
s = ((&queue_head)->tqh_first);
//用new_iten替换第一个元素
//TAILQ_REPLACE(&queue_head, s, new_item, entry);
do
{
if (((new_item)->entry.tqe_next = (s)->entry.tqe_next) != 0)
(new_item)->entry.tqe_next->entry.tqe_prev = &(new_item)->entry.tqe_next;
else
(&queue_head)->tqh_last = &(new_item)->entry.tqe_next;
(new_item)->entry.tqe_prev = (s)->entry.tqe_prev;
*(new_item)->entry.tqe_prev = (new_item);
}while(0);
printf("now, print again\n");
i = 0;
//TAILQ_FOREACH(p, &queue_head, entry)//用foreach遍历所有元素
for((p) = ((&queue_head)->tqh_first); (p) != 0;
(p) = ((p)->entry.tqe_next))
{
printf("the %dth entry is %d\n", i, p->value);
}
//p = TAILQ_LAST(&queue_head, queue_head_t);
p = (*(((struct queue_head_t *)((&queue_head)->tqh_last))->tqh_last));
printf("last is %d\n", p->value);
//p = TAILQ_PREV(p, queue_head_t, entry);
p = (*(((struct queue_head_t *)((p)->entry.tqe_prev))->tqh_last));
printf("the entry before last is %d\n", p->value);
}
(4)C/C++中指针操作
#include
struct item_t
{
int a;
int b;
int c;
};
struct entry_t
{
int a;
int b;
};
int main()
{
struct item_t item = { 1, 2, 3};
entry_t *p = (entry_t*)(&item.b);
printf("a = %d, b = %d\n", p->a, p->b);
return 0;
}
其实此时
p = (*(((struct queue_head_t *)((&queue_head)->tqh_last))->tqh_last))
也就是((&queue_head)->tqh_last) + 4(32位系统指针大小4字节)