我们知道在Nginx中许多地方需要用到定时器,典型的就是当accept接受连接后,如果在一定时间内还未收到用户请求,就超时断开连接。那这个是如何实现的呢?这个问题我是相当的有兴趣。先说点题外话,有一天晚上,坐在床上看电影,但是又到了睡觉的时间,我怕管不住自己,于是就想让电脑定时关机,印象中好像cron还是at可以执行定时任务,但是自己又不会用,打开man page看那些英文觉得太麻烦,干脆自己做一个定时器算了,于是花了半个多小时终于搞定了,但是问题是这个定时器只能处理单任务,倘若有很多任务都要定时,并及时处理超时任务该怎么办呢?在Nginx中找到了答案,于是在之前那个迷你版的Nginx上增加了这个超时的机制。不过我的实现跟Nginx的又有所区别,Nginx是用红黑树来管理定时器的,我为了简单,用了一个排序的双链表管理定时器,其实原理是一样的。
先给出这个双链的接口
- #ifndef _LIST_H_INCLUDED__
- #define _LIST_H_INCLUDED__
- struct list_node {
- struct list_node *next, *prev;
- };
- static inline void
- list_init(struct list_node *node)
- {
- node->next = node->prev = node;
- }
- static inline void
- __list_add(struct list_node *prev, struct list_node *next, struct list_node *node)
- {
- prev->next = node;
- node->prev = prev;
- next->prev = node;
- node->next = next;
- }
- static inline void
- list_add_prev(struct list_node *cur, struct list_node *node)
- {
- __list_add(cur->prev, cur, node);
- }
- static inline void
- list_add_next(struct list_node *cur, struct list_node *node)
- {
- __list_add(cur, cur->next, node);
- }
- static inline void
- list_add_tail(struct list_node *head, struct list_node *node)
- {
- list_add_prev(head, node);
- }
- static inline void
- list_add_head(struct list_node *head, struct list_node *node)
- {
- list_add_next(head, node);
- }
- static inline void
- __list_del(struct list_node *prev, struct list_node *next)
- {
- prev->next = next;
- next->prev = prev;
- }
- static inline void
- list_del(struct list_node *node)
- {
- __list_del(node->prev, node->next);
- node->prev = node->next = NULL;
- }
- static inline int
- list_empty(struct list_node *head)
- {
- return head->next == head->prev && head->next == head;
- }
- #define offsetof(ptype, element) \
- ((int) &(((ptype) 0)->element))
- #define container_of(list, ptype, element) \
- ((ptype) ((char *) (list) - offsetof(ptype, element)))
- #define list_entry(list, ptype, element) \
- container_of(list, ptype, element)
- #define list_first_entry(head, ptype, element) \
- list_entry((head)->next, ptype, element)
- #define list_for_each_entry(node, head, element) \
- for (node = list_entry((head)->next, typeof(node), element); \
- &(node->element) != (head); \
- node = list_entry(node->element.next, typeof(node), element))
- #endif
接下来就是定时器的接口了
- #ifndef _TIMER_H_INCLUDED__
- #define _TIMER_H_INCLUDED__
- #include "httpd.h"
- struct timer {
- int key;
- struct list_node list;
- };
- static inline void
- list_timer_del(struct timer *p)
- {
- list_del(&p->list);
- }
- static inline void
- list_timer_add(struct timer *timer, struct timer *p)
- {
- struct timer *t;
- list_init(&p->list);
- list_for_each_entry(t, &timer->list, list) {
- if (t->key >= p->key) break;
- }
- list_add_prev(&t->list, &p->list);
- }
- static inline struct timer *
- list_timer_entry(struct list_node *list)
- {
- return list_entry(list, struct timer *, list);
- }
- static inline struct timer *
- list_timer_first_entry(struct timer *timer)
- {
- return list_first_entry(&timer->list, struct timer *, list);
- }
- static inline int
- list_timer_empty(struct timer *timer)
- {
- return list_empty(&timer->list);
- }
- static inline struct timer *
- list_min_timer(struct timer *timer)
- {
- if (list_timer_empty(timer))
- return NULL;
- return list_timer_first_entry(timer);
- }
- #endif
- #include "httpd.h"
- #define TIMER_LAZY_DELAY 300
- struct timer httpd_timer;
- void
- httpd_event_timer_init(void)
- {
- list_init(&httpd_timer.list);
- }
- void
- httpd_timer_del(event_t *ev)
- {
- list_timer_del(&ev->timer);
- ev->timer_set = 0;
- }
- void
- httpd_timer_add(event_t *ev, int msec)
- {
- int diff;
- uint key;
- key = httpd_current_msec + msec;
-
- if (ev->timer_set) {
-
- diff = key - ev->timer.key;
- if (abs(diff) < TIMER_LAZY_DELAY)
- return;
- httpd_timer_del(ev);
- }
- ev->timer.key = key;
-
- list_timer_add(&httpd_timer, &ev->timer);
-
- ev->timer_set = 1;
- }
- uint
- httpd_find_timer(void)
- {
- int timer;
- struct timer *p;
- if (list_timer_empty(&httpd_timer))
- return (uint_t)-1;
- p = list_timer_entry(httpd_timer.list.next);
- timer = p->key - httpd_current_msec;
- return timer > 0 ? timer : 0;
- }
- void
- httpd_event_expire_timers(void)
- {
- struct timer *p;
- event_t *ev;
- for ( ; p = list_timer_first_entry(&httpd_timer); ) {
- if ((int)p->key - (int)httpd_current_msec <= 0) {
-
- ev = list_entry(p, event_t *, timer);
-
- list_timer_del(p);
-
- ev->timer_set = 0;
- ev->timedout = 1;
-
- ev->handler(ev);
- }
- break;
- }
- }
- #include "httpd.h"
- uint_t httpd_current_msec;
- void
- httpd_time_update(void)
- {
- struct timeval tv;
- gettimeofday(&tv, NULL);
- httpd_current_msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;
- }
我们再来看看整个处理流程是怎么样的呢?
- #include "httpd.h"
- int ep = -1;
- int nevents;
- struct epoll_event *event_list;
- void
- epoll_init(void)
- {
- if ((ep = epoll_create(1024)) < 0)
- err_sys("epoll create error");
- event_list = calloc(1024, sizeof(struct epoll_event));
- nevents = 1024;
- }
- void
- epoll_add_event(event_t *ev, int event, int flag)
- {
- struct epoll_event ee;
- struct connection *c;
- c = ev->connection;
- ee.events = event | flag;
- ee.data.ptr = c;
- if (epoll_ctl(ep, EPOLL_CTL_ADD, c->fd, &ee) < 0)
- err_sys("epoll ctl error");
- ev->active = 1;
- }
- void
- epoll_process_events(void)
- {
- int i, events, revents, timer;
- connection_t *c;
- event_t *rev, *wev;
- timer = httpd_find_timer();
- events = epoll_wait(ep, event_list, nevents, timer);
- httpd_time_update();
- for (i = 0; i < events; i++) {
-
- c = event_list[i].data.ptr;
-
- revents = event_list[i].events;
-
- rev = c->rev;
- if ((revents & EPOLLIN) && rev->active) {
- rev->handler(rev);
- }
- wev = c->wev;
- if ((revents & EPOLLOUT) && wev->active) {
- wev->handler(wev);
- }
- }
- httpd_event_expire_timers();
- }
阅读(1019) | 评论(0) | 转发(0) |