[libevent源码分析] event_add
来源:互联网 发布:钱龙软件 编辑:程序博客网 时间:2024/06/05 02:43
event_add 把event往当前event中的ev_base追加,如果需要定时,那么tv不能为空
intevent_add(struct event *ev, const struct timeval *tv){struct event_base *base = ev->ev_base; //event_add 会把event加入到他的ev_base成员里const struct eventop *evsel = base->evsel; //对应linux的epoll相关函数void *evbase = base->evbase; //对应linux为 struct epollop /* * struct epollop * { * struct evepoll *fds; //分配nfiles个evepoll对象 * int nfds; //支持的最大软限制数的句柄(上面的数组个数) * struct epoll_event* events; //分配nfiles(软限制数) epoll_event对象 * int nevents; //nevents保存着上面events个数 * int epfd; //保存着epoll_create的句柄 * } */int res = 0;event_debug(( "event_add: event: %p, %s%s%scall %p", ev, ev->ev_events & EV_READ ? "EV_READ " : " ", ev->ev_events & EV_WRITE ? "EV_WRITE " : " ", tv ? "EV_TIMEOUT " : " ", ev->ev_callback));assert(!(ev->ev_flags & ~EVLIST_ALL));if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) {if (min_heap_reserve(&base->timeheap,1 + min_heap_size(&base->timeheap)) == -1)return (-1); /* ENOMEM == errno */}if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) && !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {res = evsel->add(evbase, ev); //注册or修改事件到epoll中去if (res != -1)event_queue_insert(base, ev, EVLIST_INSERTED); //把ev插入到eventqeue中去} /* * 如果说插入事件成功同时设置了超时时间 */if (res != -1 && tv != NULL) {struct timeval now; /* *如果已经存在了超时时间,那么就删除这个超时节点 */ if (ev->ev_flags & EVLIST_TIMEOUT)event_queue_remove(base, ev, EVLIST_TIMEOUT);/* Check if it is active due to a timeout. Rescheduling * this timeout before the callback can be executed * removes it from the active list. */if ((ev->ev_flags & EVLIST_ACTIVE) && (ev->ev_res & EV_TIMEOUT)) {/* See if we are just active executing this * event in a loop */if (ev->ev_ncalls && ev->ev_pncalls) {/* Abort loop */*ev->ev_pncalls = 0;}event_queue_remove(base, ev, EVLIST_ACTIVE);}gettime(base, &now);evutil_timeradd(&now, tv, &ev->ev_timeout);event_debug(( "event_add: timeout in %ld seconds, call %p", tv->tv_sec, ev->ev_callback));event_queue_insert(base, ev, EVLIST_TIMEOUT);//加入到最小超时堆中去}return (res);}如果新加的事件不为空,当前事件不是timeout时间,就为他在时间最小堆上分配一块可用空间(如果最小堆有空间,就不会重新分配)
关于最小堆可以连接我的githup,libevent用最小堆来管理定时事件,根节点永远是最小的而且算法时间最小
如果事件是EV_READ、EV_WRITE、EV_SIGNAL,并且事件不是插入or激活状态,那么就加入事件
以epoll为例
static intepoll_add(void *arg, struct event *ev){struct epollop *epollop = arg;struct epoll_event epev = {0, {0}};struct evepoll *evep;int fd, op, events; /* * 信号的原理 * 1 设置信号处理函数,保存原来的信号处理函数到event的ev_base->sh_old中去 * 2 如果是首次添加信号,那么需要为所有信号追加一个信号事件来源自event->ev_base->ev_signal * 同时设置event->ev_base->ev_signal->ev_signal_added,表示信号回调事件已经设置了 * 3 同时把事件追加到event->ev_base->ev_signal->evsigevents[signno]链表中去 */if (ev->ev_events & EV_SIGNAL) //如果是加入的信号return (evsignal_add(ev));fd = ev->ev_fd;if (fd >= epollop->nfds) {/* Extent the file descriptor array as necessary */ //如果说当前的epoll中已经满足不了新加入的句柄if (epoll_recalc(ev->ev_base, epollop, fd) == -1)return (-1);}evep = &epollop->fds[fd];op = EPOLL_CTL_ADD;events = 0; //如果事件之前是可读if (evep->evread != NULL) {events |= EPOLLIN;op = EPOLL_CTL_MOD;} //如果事件之前是可写if (evep->evwrite != NULL) {events |= EPOLLOUT;op = EPOLL_CTL_MOD;} //新加入事件设置的是可读事件if (ev->ev_events & EV_READ)events |= EPOLLIN; //新加入事件设置的是可写事件if (ev->ev_events & EV_WRITE)events |= EPOLLOUT;epev.data.ptr = evep;epev.events = events;if (epoll_ctl(epollop->epfd, op, ev->ev_fd, &epev) == -1)return (-1);/* Update events responsible */if (ev->ev_events & EV_READ)evep->evread = ev;if (ev->ev_events & EV_WRITE)evep->evwrite = ev;return (0);}
信号追加,libevent对信号采用sockpair 创建两个互连socket,信号激发写可写sock,可读加入到事件循环中去
根据信号ev_base->sig.evsigcaught[signumber]来判断该信号是否发生,下面是信号追加函数
intevsignal_add(struct event *ev){int evsignal;struct event_base *base = ev->ev_base; //信号event的basestruct evsignal_info *sig = &ev->ev_base->sig; //信号在ev_base的管理结构if (ev->ev_events & (EV_READ|EV_WRITE)) //信号事件不支持可读可写事件event_errx(1, "%s: EV_SIGNAL incompatible use", __func__);evsignal = EVENT_SIGNAL(ev); //信号的fd就是信号numberassert(evsignal >= 0 && evsignal < NSIG); //信号不能超过NSIG这个数if (TAILQ_EMPTY(&sig->evsigevents[evsignal])) { //如果说该信号链表为空event_debug(("%s: %p: changing signal handler", __func__, ev)); //设置信号处理函数,同时,保存原来的信号处理函数到ev_base->sh_old中去if (_evsignal_set_handler( base, evsignal, evsignal_handler) == -1)return (-1);/* catch signals if they happen quickly */evsignal_base = base;if (!sig->ev_signal_added) {if (event_add(&sig->ev_signal, NULL))return (-1);sig->ev_signal_added = 1; //加入epoll_wait中去}} //把ev->ev_signal_next加入到sig->evsigevents[evsignal]的链表末端/* multiple events may listen to the same signal */TAILQ_INSERT_TAIL(&sig->evsigevents[evsignal], ev, ev_signal_next);return (0);}
信号回调函数
/* * 所有的信号都通用一个信号处理函数 */static voidevsignal_handler(int sig){int save_errno = errno; //保存错误码if (evsignal_base == NULL) {event_warn("%s: received signal %d, but have no base configured",__func__, sig);return;}evsignal_base->sig.evsigcaught[sig]++; //引起的信号number++evsignal_base->sig.evsignal_caught = 1; //设置信号标志,表示信号已经触发#ifndef HAVE_SIGACTIONsignal(sig, evsignal_handler);#endif/* Wake up our notification mechanism */send(evsignal_base->sig.ev_signal_pair[0], "a", 1, 0); //通过写sockpair写数据来触发epoll_wait返回errno = save_errno;}
定时事件采用最小堆,定时值+当前事件最为左后超时时间,加入到最小堆中去,主要在event_base_dispatch中使用
libeven 整个源码分析和sample都在我的 githup
0 0
- [libevent源码分析] event_add
- [libevent源码分析] event_add
- libevent源码分析--event_add()函数
- [libevent]event_add()
- libevent事件(三)---event_add和epoll_add
- libevent源码分析
- libevent源码分析
- libevent源码分析
- libevent源码分析
- libevent源码分析
- libevent源码分析
- libevent源码分析
- libevent源码分析
- libevent源码分析
- libevent源码分析
- libevent源码分析
- libevent源码分析--epoll_dispatch()
- Libevent源码分析-----开篇
- [libevent源码分析] event_init
- 在 BeagleBoard Black 上快速安装 Android 系统 与 使用adb
- [libevent源码分析] event_set
- 关于stringstream重复使用时的问题2
- Clone Graph
- [libevent源码分析] event_add
- [libevent源码分析] event_base_dispatch
- jQuery.extend 函数详解
- Amazon电面题目-sort 100 IP addresses
- 2sum
- Erlang的socket 编程简例
- BZOJ 2819 Nim (树链剖分+线段树)
- DNA排序 poj
- 34个Android常用adb shell命令汇总