Chinaunix首页 | 论坛 | 博客
  • 博客访问: 188663
  • 博文数量: 27
  • 博客积分: 725
  • 博客等级: 上士
  • 技术积分: 347
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-04 09:01
文章分类

全部博文(27)

文章存档

2012年(15)

2011年(12)

分类: LINUX

2012-03-27 19:59:36

我们知道在Nginx中许多地方需要用到定时器,典型的就是当accept接受连接后,如果在一定时间内还未收到用户请求,就超时断开连接。那这个是如何实现的呢?这个问题我是相当的有兴趣。先说点题外话,有一天晚上,坐在床上看电影,但是又到了睡觉的时间,我怕管不住自己,于是就想让电脑定时关机,印象中好像cron还是at可以执行定时任务,但是自己又不会用,打开man page看那些英文觉得太麻烦,干脆自己做一个定时器算了,于是花了半个多小时终于搞定了,但是问题是这个定时器只能处理单任务,倘若有很多任务都要定时,并及时处理超时任务该怎么办呢?在Nginx中找到了答案,于是在之前那个迷你版的Nginx上增加了这个超时的机制。不过我的实现跟Nginx的又有所区别,Nginx是用红黑树来管理定时器的,我为了简单,用了一个排序的双链表管理定时器,其实原理是一样的。
 
先给出这个双链的接口

  1. #ifndef _LIST_H_INCLUDED__
  2. #define _LIST_H_INCLUDED__

  3. struct list_node {
  4.     struct    list_node        *next, *prev;
  5. };

  6. static inline void
  7. list_init(struct list_node *node)
  8. {
  9.     node->next = node->prev = node;
  10. }

  11. static inline void
  12. __list_add(struct list_node *prev, struct list_node *next, struct list_node *node)
  13. {
  14.     prev->next = node;
  15.     node->prev = prev;

  16.     next->prev = node;
  17.     node->next = next;
  18. }

  19. static inline void
  20. list_add_prev(struct list_node *cur, struct list_node *node)
  21. {
  22.     __list_add(cur->prev, cur, node);
  23. }

  24. static inline void
  25. list_add_next(struct list_node *cur, struct list_node *node)
  26. {
  27.     __list_add(cur, cur->next, node);
  28. }

  29. static inline void
  30. list_add_tail(struct list_node *head, struct list_node *node)
  31. {
  32.     list_add_prev(head, node);
  33. }

  34. static inline void
  35. list_add_head(struct list_node *head, struct list_node *node)
  36. {
  37.     list_add_next(head, node);
  38. }

  39. static inline void
  40. __list_del(struct list_node *prev, struct list_node *next)
  41. {
  42.     prev->next = next;
  43.     next->prev = prev;
  44. }

  45. static inline void
  46. list_del(struct list_node *node)
  47. {
  48.     __list_del(node->prev, node->next);
  49.     node->prev = node->next = NULL;
  50. }

  51. static inline int
  52. list_empty(struct list_node *head)
  53. {
  54.     return head->next == head->prev && head->next == head;
  55. }

  56. #define offsetof(ptype, element) \
  57.     ((int) &(((ptype) 0)->element))

  58. #define container_of(list, ptype, element) \
  59.     ((ptype) ((char *) (list) - offsetof(ptype, element)))

  60. #define list_entry(list, ptype, element) \
  61.     container_of(list, ptype, element)

  62. #define list_first_entry(head, ptype, element) \
  63.     list_entry((head)->next, ptype, element)

  64. #define list_for_each_entry(node, head, element) \
  65.     for (node = list_entry((head)->next, typeof(node), element); \
  66.             &(node->element) != (head); \
  67.             node = list_entry(node->element.next, typeof(node), element))

  68. #endif
接下来就是定时器的接口了

  1. #ifndef _TIMER_H_INCLUDED__
  2. #define _TIMER_H_INCLUDED__

  3. #include "httpd.h"

  4. struct timer {
  5.     int                    key;
  6.     struct list_node    list;
  7. };

  8. static inline void
  9. list_timer_del(struct timer *p)
  10. {
  11.     list_del(&p->list);
  12. }

  13. static inline void
  14. list_timer_add(struct timer *timer, struct timer *p)
  15. {
  16.     struct timer *t;

  17.     list_init(&p->list);

  18.     list_for_each_entry(t, &timer->list, list) {
  19.         if (t->key >= p->key) break;
  20.     }

  21.     list_add_prev(&t->list, &p->list);
  22. }

  23. static inline struct timer *
  24. list_timer_entry(struct list_node *list)
  25. {
  26.     return list_entry(list, struct timer *, list);
  27. }

  28. static inline struct timer *
  29. list_timer_first_entry(struct timer *timer)
  30. {
  31.     return list_first_entry(&timer->list, struct timer *, list);
  32. }

  33. static inline int
  34. list_timer_empty(struct timer *timer)
  35. {
  36.     return list_empty(&timer->list);
  37. }

  38. static inline struct timer *
  39. list_min_timer(struct timer *timer)
  40. {
  41.     if (list_timer_empty(timer))
  42.         return NULL;

  43.     return list_timer_first_entry(timer);
  44. }

  45. #endif

  1. #include "httpd.h"

  2. #define TIMER_LAZY_DELAY    300

  3. struct timer httpd_timer;

  4. void
  5. httpd_event_timer_init(void)
  6. {
  7.     list_init(&httpd_timer.list);
  8. }

  9. void
  10. httpd_timer_del(event_t *ev)
  11. {
  12.     list_timer_del(&ev->timer);
  13.     ev->timer_set = 0;
  14. }

  15. void
  16. httpd_timer_add(event_t *ev, int msec)
  17. {
  18.     int     diff;
  19.     uint    key;

  20.     key = httpd_current_msec + msec;
  21.     
  22.     if (ev->timer_set) {
  23.         
  24.         diff = key - ev->timer.key;
  25.         if (abs(diff) < TIMER_LAZY_DELAY)
  26.             return;

  27.         httpd_timer_del(ev);
  28.     }

  29.     ev->timer.key = key;
  30.     
  31.     list_timer_add(&httpd_timer, &ev->timer);
  32.     
  33.     ev->timer_set = 1;
  34. }

  35. uint
  36. httpd_find_timer(void)
  37. {
  38.     int             timer;
  39.     struct timer *p;

  40.     if (list_timer_empty(&httpd_timer))
  41.         return (uint_t)-1;

  42.     p = list_timer_entry(httpd_timer.list.next);

  43.     timer = p->key - httpd_current_msec;

  44.     return timer > 0 ? timer : 0;
  45. }

  46. void
  47. httpd_event_expire_timers(void)
  48. {
  49.     struct timer     *p;
  50.     event_t            *ev;

  51.     for ( ; p = list_timer_first_entry(&httpd_timer); ) {

  52.         if ((int)p->key - (int)httpd_current_msec <= 0) {
  53.         
  54.             ev = list_entry(p, event_t *, timer);
  55.             
  56.             list_timer_del(p);
  57.             
  58.             ev->timer_set = 0;
  59.             ev->timedout = 1;
  60.             
  61.             ev->handler(ev);
  62.         }

  63.         break;
  64.     }
  65. }

  1. #include "httpd.h"

  2. uint_t    httpd_current_msec;

  3. void
  4. httpd_time_update(void)
  5. {
  6.     struct timeval tv;

  7.     gettimeofday(&tv, NULL);

  8.     httpd_current_msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;
  9. }
我们再来看看整个处理流程是怎么样的呢?

  1. #include "httpd.h"

  2. int                 ep = -1;
  3. int                    nevents;
  4. struct epoll_event    *event_list;

  5. void
  6. epoll_init(void)
  7. {
  8.     if ((ep = epoll_create(1024)) < 0)
  9.         err_sys("epoll create error");

  10.     event_list = calloc(1024, sizeof(struct epoll_event));
  11.     nevents = 1024;
  12. }

  13. void
  14. epoll_add_event(event_t *ev, int event, int flag)
  15. {
  16.     struct epoll_event      ee;
  17.     struct connection    *c;

  18.     c = ev->connection;

  19.     ee.events = event | flag;
  20.     ee.data.ptr = c;

  21.     if (epoll_ctl(ep, EPOLL_CTL_ADD, c->fd, &ee) < 0)
  22.         err_sys("epoll ctl error");

  23.     ev->active = 1;
  24. }

  25. void
  26. epoll_process_events(void)
  27. {
  28.     int              i, events, revents, timer;
  29.     connection_t    *c;
  30.     event_t            *rev, *wev;

  31.     timer = httpd_find_timer();

  32.     events = epoll_wait(ep, event_list, nevents, timer);

  33.     httpd_time_update();

  34.     for (i = 0; i < events; i++) {
  35.     
  36.         c = event_list[i].data.ptr;
  37.         
  38.         revents = event_list[i].events;
  39.         
  40.         rev = c->rev;

  41.         if ((revents & EPOLLIN) && rev->active) {
  42.             rev->handler(rev);
  43.         }

  44.         wev = c->wev;

  45.         if ((revents & EPOLLOUT) && wev->active) {
  46.             wev->handler(wev);
  47.         }
  48.     }

  49.     httpd_event_expire_timers();
  50. }




 
阅读(3391) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~