lwip协议栈中超时定时器实现原理
? lwip協議棧中超時定時器實現原理
1,超時定時器存在的目的:
??????tcpip協議中存在很多需要定時處理的任務,包括一次性超時處理和周期性超時處理。
??????以tcp傳輸為例,每條連接總共需要建立七個定時器,依次為:
???? ?1)“連接建立”定時器。
????????????? 如果建立連接啟動后75秒內沒收到響應,則中止建立。
????? 2)“重傳”定時器
???????????? ?在tcp發送某個數據段時設定,如果定時器超時了還沒接收到對端的確認,則重傳數據段。重傳定時器值是動態計算的,與???????? ?RTT的估算密切相關,且還取決于該報文已被重傳的次數。
???? 3)“延遲ACK”定時器
???????????? tcp收到必須確認但無需馬上發出確認的數據時設定, 如果在200ms內,有數據要在該連接上發送,延遲的ACK隨數據一并發??????????送會對端,若200ms后仍未發出,則定時器超時,此時需要發送一個立即確認。
?????4)“持續”定時器
???????????? 在連接對端通知接收窗口為0(緩存無足夠空間),阻止發送端繼續發送數據時設定。發送端停止發送數據,啟動持續定時???? ? ????? ? ? ?器,超時后向對端發送1字節的數據,判斷對端接收窗是否已打開。
?????5)“保活”定時器
???????????? 定時器在tcp控制塊的so_options字段設置了SOF_KEEPALIVE選項時生效。如果連接的空閑時間超過2小時,保活定時器超 ? ???? ? ? ?時,此時向對端發送連接探測報文,強迫對端響應。如果收到響應,tcp可確認對端主機工作正常,如果收到RST復位響應,可確 ? ? ?????????? ?認對端主機已重啟,如果連續若干次保活測試都未收到響應,則tcp假定對端主機已崩潰,但無法區分是主機故障還是線路故障。
???? 6)“FIN_WAIT_2”定時器
?????7)“TIME_WAIT”定時器(也稱2MSL定時器)
2,lwip中超時定時器設計架構:
????? 這里不討論各個定時器與tcp協議有關的超時如何處理,只講超時定時器本身的設計。
????? 在lwip中,超時定時器代碼實現在 src/core/timers.c中
????? 超時定時器是按鏈表的形式進行組織的,并且按時間長短進行排序,時間最短的永遠在最前面。定時器使用結構體struct sys_timeo進行定義,結構體定義如下:
??????struct sys_timeo {
? ????????????struct sys_timeo *next;???? ?//指向下一個定時器
? ????????????u32_t time;????????????????????????//定時值(ms),在加入鏈表時這個值會進行調整
? ????????????sys_timeout_handler h;????//定時器回調函數
? ????????????void *arg;????????????????????????? //回調函數傳入參數
????????#if LWIP_DEBUG_TIMERNAMES
? ????????????const char* handler_name;????????????//回調函數名稱,調試用
????????#endif /* LWIP_DEBUG_TIMERNAMES */
???????};
???????添加超時定時器,函數如下:
void ?sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg)
{
? struct sys_timeo *timeout, *t;
? /* 每個定時器都從對應的內存池中分配數據結構 */
? timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT);
? if (timeout == NULL) {
? ? LWIP_ASSERT("sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty", timeout != NULL);
? ? return;
? }
? timeout->next = NULL;
? timeout->h = handler;
? timeout->arg = arg;
? timeout->time = msecs;
#if LWIP_DEBUG_TIMERNAMES
? timeout->handler_name = handler_name;
? LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" handler=%s arg=%p\n",
? ? (void *)timeout, msecs, handler_name, (void *)arg));
#endif /* LWIP_DEBUG_TIMERNAMES */
? /* 如果創建的是第一個定時器,則不用特殊處理,next_timeout是一個全局指針,指向定時器鏈表中第一個定時器?*/
? if (next_timeout == NULL) {
? ? next_timeout = timeout;
? ? return;
? }
? /* 從第二個定時器開始就要添加到鏈表中,添加原則是定時最短的定時器始終在前面,如果新添加的定時器時長小于當前鏈首
?????定時器,則新添加的定時器成為鏈首,舊的鏈首定時器的定時值要減去新鏈首定時器定時值,這樣舊定時器時長不變,如果新
? ? ?添加的定時器大于等于當前鏈首定時器的時長,則要在整個鏈表里逐個比較,最終將其插入到比其短的定時器之后,比其長的
? ? ?定時器之前,當然其后定時器的定時值也要進行調整,其前的定時器無需調整 */
? if (next_timeout->time > msecs) {
? ? next_timeout->time -= msecs;
? ? timeout->next = next_timeout;
? ? next_timeout = timeout;
? } else {
? ? for(t = next_timeout; t != NULL; t = t->next) {
? ? ? timeout->time -= t->time;
? ? ? if (t->next == NULL || t->next->time > timeout->time) {
? ? ? ? if (t->next != NULL) {
? ? ? ? ? t->next->time -= timeout->time;
? ? ? ? }
? ? ? ? timeout->next = t->next;
? ? ? ? t->next = timeout;
? ? ? ? break;
? ? ? }
? ? }
? }
}
?????鏈表中定時器總是按時長升序進行排列,其定時值調整算法為:
?????????????Timer(x). time = Timer(x-1).time +?Timer(x-2).time?+ ......
???? 假如有4個定時器,如下:
???? ? ? ? ??Timer1.time = 10
???? ? ? ? ? Timer2.time = 5
? ? ?? ? ? ??Timer3.time = 20
? ? ?? ? ? ??Timer4time = 10
? ? ?按1-4順序都添加到鏈表中后,定時器值如下: ?
?????????????Timer1.time = 5
?????????????Timer2.time = 5
?????????????Timer3.time = 10
?????????????Timer4.time = 0
?????鏈接關系變為:
?????????????Timer2.next = Timer1
? ? ?????????Timer1.next = Timer4
? ? ?????????Timer4.next = Timer3
? ? ?????????Timer3.next = NULL
? ? ?此時,全局指針變量next_timeout指向Timer2。
3,lwip中超時定時器應用:
???? 不管是否有os支持,超時定時器都可以使用。lwip中如下兩個函數可以實現對超時的處理:
???? ?void?sys_check_timeouts(void)
????? 裸機應用程序在外部周期性調用該函數,每次進來檢查定時器鏈表上定時最短的定時器是否到期,如果沒有到期,直接退出該函數,否則,執行該定時器回調函數,并從鏈表上刪除該定時器,然后繼續檢查下一個定時器,直到沒有一個定時器到期退出
? ? ??void?sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg)
???? ?這個函數可在os線程中循環調用,主要是等待mbox消息,并可阻塞,如果等待mbox時超時,則會同時執行超時事件處理,即調用定時器回調函數,如果一直沒有mbox消息,則會永久性地循環將所有超時定時器檢查一遍,一舉兩得。
????? lwip中tcpip線程就是靠這種方法,即處理了上層及底層的mbox消息,同時處理了所有需要定時處理的事件。
總結
以上是生活随笔為你收集整理的lwip协议栈中超时定时器实现原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: FreeRTOS — 消息队列
- 下一篇: buntu linux下建立stm32开