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