在說主題之前先來說一下linux中timer的實現:在linux中,設定定時器,是通過每次系統定時器時鐘的中斷處理程式來設定相應的軟中斷位,然後通過這個中斷處理程式掃瞄系統中所有掛起的定時器,如果發現哪個定時器超時了就呼叫相應的處理函式,也就說linux定時器是通過系統中斷實現的。
在nginx中,timer是自己實現的,而且實現的方法完全不同,它是通過乙個紅黑樹去維護所有的time節點。然後在工作程序中每一迴圈都會呼叫ngx_process_events_and_timers函式,這個函式中又會呼叫處理定時器的函式ngx_event_expire_timers,這個函式每一次都會從紅黑樹中取最小的時間值,判斷是否超時,超時就執行他們的函式,直到取出的不超時為止。類似的做法還有libevent中的小根堆維護time的思想,這兩種思想是類似的,而且也能把定時器的超時時間和一些i/o事件統一到了工作程序之中。
來看看定時器的初始化ngx_event.c中:
/*初始化計時器,此處將會建立起一顆紅黑色,來維護計時器。*/
if (ngx_event_timer_init(cycle->log) == ngx_error)
//timer的初始化
ngx_int_t
ngx_event_timer_init(ngx_log_t *log)
ngx_event_timer_mutex = ngx_mutex_init(log, 0);
if (ngx_event_timer_mutex == null)
#endif
return ngx_ok;
}
這個函式就是呼叫 ngx_rbtree_init來初始化一顆nginx自己實現的紅黑樹。
接下來就是如何去定義乙個timer事件了:
就是利用下面這個函式,將乙個定時事件的超時時間值加入到紅黑樹中即可,與之對應的還有乙個從紅黑樹中刪除的操作函式:ngx_event_del_timer
//將乙個定時時間加入到紅黑樹中,交給紅黑樹維護
static ngx_inline void
ngx_event_add_timer(ngx_event_t *ev, ngx_msec_t timer) //timer說白了就是乙個int型別的值,表示超時的時間值,紅黑樹節點的key就是這個timer
ngx_del_timer(ev);
}ev->timer.key = key;
ngx_log_debug3(ngx_log_debug_event, ev->log, 0,
"event timer add: %d: %m:%m",
ngx_event_ident(ev->data), timer, ev->timer.key);
//需要進行加鎖操作保證執行緒安全
ngx_mutex_lock(ngx_event_timer_mutex);
ngx_rbtree_insert(&ngx_event_timer_rbtree, &ev->timer); //將定時事件的timer域插入到紅黑樹當中
ngx_mutex_unlock(ngx_event_timer_mutex);
ev->timer_set = 1;
}
那麼nginx是如何處理定時事件的:
在ngx_process_events_and_timers中對定時事件處理,先去把當前紅黑樹中最小的事件找到,然後傳遞給epoll的wait,這一點保證epoll可以處理超時事件,因為如果epoll收到超時事件卻沒有收到這個那麼超時事件就不會得到處理了:
if (ngx_timer_resolution) else
#endif
}
尋找最小的節點ngx_event_find_timer:
//用於返回當前紅黑樹當中的超時時間,說白了就是返回紅黑樹中最左邊的元素的超時時間
ngx_msec_t
ngx_event_find_timer(void)
ngx_mutex_lock(ngx_event_timer_mutex);
root = ngx_event_timer_rbtree.root;
sentinel = ngx_event_timer_rbtree.sentinel;
node = ngx_rbtree_min(root, sentinel); //找到紅黑樹中key最小的節點
ngx_mutex_unlock(ngx_event_timer_mutex);
timer = (ngx_msec_int_t) (node->key - ngx_current_msec);//返回的是還剩下的超時時間
return (ngx_msec_t) (timer > 0 ? timer : 0);
}
在ngx_process_events_and_timers函式中獲取這個值,因為這個值是要用來設定epoll的wait時間的,保證epoll在處理的時候,最小的超時事件既然能及時得到處理,其他就都能都得到處理了,否則,已經超時了,epoll還未處理,那也就不能達到定時的目的了.
接下來:
就是說epoll wait事件是耗時的,在耗時的時間中,已經超時的就應該被刪除,同時呼叫相應的處理函式處理.
/*delta是上文對epoll wait事件的耗時統計,存在毫秒級的耗時
就對所有事件的timer進行檢查,如果time out就從timer rbtree中,
刪除到期的timer,同時呼叫相應事件的handler函式完成處理。
*/
if (delta)
在ngx_process_events_and_timers中呼叫gx_event_expire_timers處理所有的定時事件,那麼為什麼要去判斷這個delta呢?因為這個值統計的是處理其餘事件的用時,如果用時超過了毫秒,那麼就應呼叫ngx_event_expire_timers這個函式處理所有的定時,否則就不會呼叫,因為剛處理完,完全沒必要再去處理一次,效能就是在這些細節中提公升上來的。
ngx_event_expire_timers函式:
void ngx_event_expire_timers(void)
node = ngx_rbtree_min(root, sentinel); //獲取key最小的節點
/* node->key <= ngx_current_time */
if ((ngx_msec_int_t) (node->key - ngx_current_msec) <= 0)
break;
}ngx_mutex_unlock(ngx_event_timer_mutex);
}
總結:
在nginx中timer的實現是通過這種從紅黑樹中選取最小定時事件的方式,利用了紅黑樹查詢的高效,再結合自己對效能的優化,將定時器融入到worker程序之中,在nginx的執行環境之中,這種方式可能比linux中斷那一套更為高效,真的是太有智慧型了!
實現C 中的紅黑樹
rbtree.h pragma once include using namespace std enum color template class t struct rbtreenode 紅黑樹節點 rbtreenode left rbtreenode right rbtreenode paren...
關於C 中的Timer
關於c 中timer類 在c 裡關於定時器類就有3個 1.定義在system.windows.forms裡 2.定義在system.threading.timer類裡 3.定義在system.timers.timer類裡 例 使用system.timers.timer類 system.timers....
MFC中timer 的使用
首先,要設定定時器。在init中 或其他的初始化函式裡 呼叫函式 uint settimer uint nidevent,定時器標記,uint nelapse,間隔時間 void callback lpfntimer hwnd,uint,uint,dword null 如settimer 1,200...