libevent事件(三)---event_add和epoll_add

今天药忘吃喽~ 2022-06-17 08:09 224阅读 0赞

libevent将定时事件、信号、描述符事件都集成到event中,并且分别维护了相关的链表。
因此,event_addevent_delete主要是操作链表,并且调用底层的api。

1. event_add函数

首先是超时事件,timer事件用一个小根堆来表示,这个堆中的最小时间可以用来select/poll/epoll参数的最大值.
另外,添加事件分为两个内容,首先会调用底层的add回调,它有可能是epoll_ctl(),然后会将该event添加到注册事件的链表中

  1. int
  2. event_add(struct event *ev, const struct timeval *tv)
  3. {
  4. struct event_base *base = ev->ev_base;//event_base
  5. const struct eventop *evsel = base->evsel;//evsel为底层的操作
  6. void *evbase = base->evbase;
  7. int res = 0;
  8. //为timer事件分配空间,min_heap_reserve的操作实际上是realloc
  9. if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) {
  10. if (min_heap_reserve(&base->timeheap,
  11. 1 + min_heap_size(&base->timeheap)) == -1)
  12. return (-1); /* ENOMEM == errno */
  13. }//reserve
  14. //添加事件
  15. if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
  16. !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
  17. res = evsel->add(evbase, ev);//底层回调函数
  18. if (res != -1)
  19. event_queue_insert(base, ev, EVLIST_INSERTED);//event插入队列
  20. }
  21. /*
  22. * we should change the timout state only if the previous event
  23. * addition succeeded.
  24. */
  25. if (res != -1 && tv != NULL) {
  26. //当上一步操作成功时,如果是timer事件将调整timer堆
  27. struct timeval now;
  28. /*
  29. * we already reserved memory above for the case where we
  30. * are not replacing an exisiting timeout.
  31. */
  32. //表明event已经在timer堆中了,删除旧的
  33. if (ev->ev_flags & EVLIST_TIMEOUT)
  34. event_queue_remove(base, ev, EVLIST_TIMEOUT);
  35. /* Check if it is active due to a timeout. Rescheduling
  36. * this timeout before the callback can be executed
  37. * removes it from the active list. */
  38. if ((ev->ev_flags & EVLIST_ACTIVE) &&
  39. (ev->ev_res & EV_TIMEOUT)) {
  40. /* See if we are just active executing this
  41. * event in a loop
  42. */
  43. if (ev->ev_ncalls && ev->ev_pncalls) {
  44. /* Abort loop */
  45. *ev->ev_pncalls = 0;
  46. }
  47. event_queue_remove(base, ev, EVLIST_ACTIVE);
  48. }
  49. gettime(base, &now);//计算时间并插入到timer的小根堆中
  50. evutil_timeradd(&now, tv, &ev->ev_timeout);//宏定义:timeout= now + tv
  51. event_queue_insert(base, ev, EVLIST_TIMEOUT);
  52. }
  53. return (res);
  54. }

epoll_add函数

上文提到,相同的接口会调用特定的回调,以epoll为例,将调用封装的epoll_add

  1. static int
  2. epoll_add(void *arg, struct event *ev)
  3. {
  4. struct epollop *epollop = arg;
  5. struct epoll_event epev = {
  6. 0, {
  7. 0}};
  8. struct evepoll *evep;
  9. int fd, op, events;
  10. if (ev->ev_events & EV_SIGNAL)
  11. return (evsignal_add(ev));
  12. fd = ev->ev_fd;
  13. if (fd >= epollop->nfds) {
  14. /* Extent the file descriptor array as necessary */
  15. if (epoll_recalc(ev->ev_base, epollop, fd) == -1)
  16. return (-1);
  17. }
  18. evep = &epollop->fds[fd];
  19. op = EPOLL_CTL_ADD;
  20. events = 0;
  21. if (evep->evread != NULL) {
  22. events |= EPOLLIN;
  23. op = EPOLL_CTL_MOD;
  24. }
  25. if (evep->evwrite != NULL) {
  26. events |= EPOLLOUT;
  27. op = EPOLL_CTL_MOD;
  28. }
  29. if (ev->ev_events & EV_READ)
  30. events |= EPOLLIN;
  31. if (ev->ev_events & EV_WRITE)
  32. events |= EPOLLOUT;
  33. epev.data.fd = fd;
  34. epev.events = events;
  35. if (epoll_ctl(epollop->epfd, op, ev->ev_fd, &epev) == -1)
  36. return (-1);
  37. /* Update events responsible */
  38. if (ev->ev_events & EV_READ)
  39. evep->evread = ev;
  40. if (ev->ev_events & EV_WRITE)
  41. evep->evwrite = ev;
  42. return (0);
  43. }

首先来看传进来的两个参数,在event_add中有如下调用evsel->add(evbase,ev),这个时候就明白evbase的作用了,在event_base_new调用中,
base->evbase=base->evsel->init(base),回调函数将最终调用epoll_init创建一个struct epollop结构体,并返回指针,为什么要这么做?
本质上来说,还是为了可移植性,对于select也会有struct selectop这样的结构体

再看epoll_add函数,将封装的struct eventstruct epollop结构体,转化为epoll认识的struct epoll_event

发表评论

表情:
评论列表 (有 0 条评论,224人围观)

还没有评论,来说两句吧...

相关阅读