0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

基于DWC_ether_qos的以太網驅動開發(fā)-LWIP的定時器模塊詳解

嵌入式USB開發(fā) ? 來源:嵌入式USB開發(fā) ? 作者:嵌入式USB開發(fā) ? 2023-09-18 09:33 ? 次閱讀

一. 前言

LWIP的定時器模塊,實現(xiàn)了通用的軟件定時器,用于內部的周期事件處理,比如arp,tcp的超時等,用戶也可以使用。這一篇來分析該模塊的實現(xiàn)。

二.代碼分析

2.1源碼

源碼位于

timeouts.c

timeouts.h

會按照如下條件編譯

#if LWIP_TIMERS && !LWIP_TIMERS_CUSTOM

即LWIP_TIMERS為1 ,LWIP_TIMERS_CUSTOM為0才會編譯,也是默認配置。

2.2數據結構

定時器的核心數據結構是一個單向鏈表,鏈表的節(jié)點如下

struct sys_timeo {


struct sys_timeo *next;


u32_t time;


sys_timeout_handler h;


void *arg;


#if LWIP_DEBUG_TIMERNAMES


const char* handler_name;


#endif /* LWIP_DEBUG_TIMERNAMES */


};

Next構成單向鏈表

time為絕對時間,即當前絕對時間滯后該值則表示定時器超時需要執(zhí)行回調函數h。

arg可以傳入參數,

handler_name是debug打印信息用。

2.3超時比較算法

定時器使用的是絕對時間,即定時器的time和當前now時間比較,time<=now則表示定時器已經超時了需要處理,否則定時器還未到時間無需處理。

但是這里會有個問題,溢出的問題,time<=now就一定表示time的時刻提前于now嗎,不一定,也可能是到了定時器值的最大值繞回了,

比如如果定時器的值是32位的,

now為0xFFFFFFFF,time為0x00000001,

time

也可能time滯后于now為2的時間 即(0x100000000-0xFFFFFFFF)+0x00000001.

我們更傾向于是后者,因為后者的時間差更小,更符合實際情況,因為我們定時時間一般都很小。

這里的實現(xiàn)是

#define LWIP_MAX_TIMEOUT 0x7fffffff

#define TIME_LESS_THAN(t, compare_to) ( (((u32_t)((t)-(compare_to))) > LWIP_MAX_TIMEOUT) ? 1 : 0 )

實際上用到的就是我們上面提到的思想,我們更傾向于時間差更小的為實際情況,

實際該算法還有專門的文章進行討論,網上可以搜到。

即定義定時器最大范圍的一半,比如32位最大范圍是00xFFFFFFFF,共0x100000000的范圍,其一半的范圍是0x80000000,即00x7fffffff,作為基準,最大就只能定時器該時間,大于該時間認為不合理,實際是繞回反向的。

這里((u32_t)((t)-(compare_to)))按照無符號32位進行計算

如果t

如果t為1, compare_to為2則((u32_t)((t)-(compare_to)))結果為0xFFFFFFFF。

實際上就是0x100000000-0x02 + 0x01.

圖片

如果t>compare_to

t為2, compare_to為1

則((u32_t)((t)-(compare_to)))結果為0x1

圖片

該表達式即可以理解為t滯后compare_to的時間(未來時間),

更形象的理解是a-b即b需要追趕多少到a,即compare_to追趕到t需要多久,有可能繞回。

如果該滯后時間比較大,大于總時間的一半即0x7fffffff我們則認為,實際不是滯后而是超前。

因為傾向于時間間隔短的符合實際情況。

所以如果t比compare_to大的非常多,大于TIME_LESS_THAN,我們也認為t不是滯后compare_to而是提前于compare_to,只是是繞回了。

1.總結

我們可以用跑圈追趕的角度來理解,即t和compare_to在環(huán)形世界賽跑,某一刻我們并不知道誰跑在前,誰跑在后,因為有可能”套圈”, 于是我們有一條假設, t和compare_to跑的速度差異不是特別大(類似我們定時器的定時時間不是特別長),所以如果t追趕到compare_to的距離大于跑道的一半我們認為不合理,所以認為是compare_to追趕t。

所以該算法能工作的前提條件是定時時間不能大于LWIP_MAX_TIMEOUT。

當然去處理查詢定時器超時的間隔也不能大于LWIP_MAX_TIMEOUT,否則由于”套多圈”無法區(qū)分。

2.4內建定時器

定義了一個數組

const struct lwip_cyclic_timer lwip_cyclic_timers[]

提供構建定時器的必要信息(注意不是定時器本身,而是提供定時器信息,sys_timeouts_init根據此創(chuàng)建定時器)

數組成員結構體如下

struct lwip_cyclic_timer {


u32_t interval_ms;


lwip_cyclic_timer_handler handler;


#if LWIP_DEBUG_TIMERNAMES


const char* handler_name;


#endif /* LWIP_DEBUG_TIMERNAMES */


};

interval_ms為定時器執(zhí)行周期,上述定時器要求是絕對時間,為什么這里是間隔時間呢,因為now+間隔時間就是絕對時間了,初始化時會自動設置。

handler是回調函數

handler_name是用于打印定時器的名字。

默認根據宏定義了一些內建的定時器

比如,使能了LWIP_ARP則使能該定時器,回調函數是etharp_tmr,間隔時間是1S。

用戶可以配置這些宏來進行定時器的使能配置和周期配置。

#if LWIP_ARP

{ARP_TMR_INTERVAL, HANDLER(etharp_tmr)},

#endif /* LWIP_ARP */

#define ARP_TMR_INTERVAL 1000

初始化sys_timeouts_init時遍歷lwip_cyclic_timers

通過sys_timeout->sys_timeout_abs動態(tài)創(chuàng)建定時器,定時器的絕對時間自動會在now基礎上增加間隔(u32_t)(sys_now() + msecs);

這里i = (LWIP_TCP ? 1 : 0),如果有LWIP_TCP則從1開始, 0的TCP定時器單獨處理,因為它不需要總是運行,沒有tcp連接就不需要該定時器了,所以手動調用tcp_timer_needed()處理。

2.5接口代碼

sys_timeouts_sleeptime

后面定時器輪詢有分析,計算定時器鏈表中,頭定時器,離當前時間的時間,

返回0表示頭定時器已經超時需要處理,返回SYS_TIMEOUTS_SLEEPTIME_INFINITE表示沒有定時器,其他值為頭定時器離現(xiàn)在的時間間隔。因為定時器是按照時間從小到大排列,所以只需要判斷頭定時器即可。

sys_restart_timeouts

以定時器鏈表第一個定時器為基準設置為now絕對時間,后續(xù)的按照和第一個定時器的偏差設置。

在長時間沒有調用sys_check_timeouts時,重新設置時基,來觸發(fā)一次時間調度。

這樣保證在長時間沒有調用sys_check_timeouts的期間導致的定時器沒有執(zhí)行,這時能彌補下執(zhí)行一次。

sys_check_timeouts

查詢定時器,從鏈表頭開始查詢,如果超時時間到則執(zhí)行對應的回調函數,并釋放定時器。

因為已經排序不需要查詢到末尾,查詢到第一個為超時的定時器即可結束,因為后面的值更大肯定不會超時。

無OS時用戶手動調用該函數

有OS時,tcpip線程自動調用。

注意定時器都是單次的,一次執(zhí)行完后會刪除,周期執(zhí)行需要重新創(chuàng)建。

這里個人覺得每次都刪除和釋放不是很好,尤其是嵌入式平臺,多了mem等操作一方面內存碎片的問題(如果使用內存池實現(xiàn)還好,如果共用堆管理則會有些影響,尤其堆本來就很小的資源受限平臺),一方面效率降低。

sys_untimeout

從定時器鏈表刪除一個定時器

sys_timeout->sys_timeout_abs

創(chuàng)建定時器,按照定時器值從小大到插入到鏈表

sys_timeouts_init

初始化內建定時器,前面已經分析過

lwip_cyclic_timer

內建定時器回調處理

由于定時器都是單次的,所以周期定時器需要重新創(chuàng)建定時器。

內建定時器時都是設置的該回調函數

sys_timeout(lwip_cyclic_timers[i].interval_ms, lwip_cyclic_timer, LWIP_CONST_CAST(void *, &lwip_cyclic_timers[i]));

通過參數再回調具體的不同的回調函數

const struct lwip_cyclic_timer *cyclic = (const struct lwip_cyclic_timer *)arg;


cyclic- >handler();

tcp_timer_needed/tcpip_tcp_timer

tcp定時器單獨處理,創(chuàng)建一個tcp定時器

tcpip_tcp_timer會根據是否有tcp連接來確認是否需要重復定時器。

2.6定時器輪詢

無OS時手動周期調用

sys_check_timeouts

有OS時在tcpip_thread線程中

TCPIP_MBOX_FETCH即tcpip_timeouts_mbox_fetch會自動調用

sys_check_timeouts。

我們來分析下tcpip_timeouts_mbox_fetch

首先sleeptime = sys_timeouts_sleeptime(); 獲取最近一個將要超時的定時器到現(xiàn)在的時間間隔,這樣mbox_fetch時就以該間隔時間作為超時時間sleeptime,這樣如果在這個超時時間之前獲取到了mbox則處理消息,下一個循環(huán)繼續(xù)重復上述處理。否則等到超時再調用sys_check_timeouts();處理定時器。

sys_timeouts_sleeptime先要判斷是否有定時器,如果next_timeout為空則說明沒有定時器需要處理則超時時間sleeptime可以設置為無限大。

如果有定時器則只需要判斷next_timeout頭定時器得time與now比較即可,因為定時器是按照time從小到大排列的,所以最先超時得肯定是頭定時器。如果next_timeout得time小于now,說明該定時器已經超時了設置為0,后面會馬上調用sys_check_timeouts()處理。

否則計算next_timeout得time減去now為間隔時間。

也就是對應

if (sleeptime == SYS_TIMEOUTS_SLEEPTIME_INFINITE) {


  UNLOCK_TCPIP_CORE();


  sys_arch_mbox_fetch(mbox, msg, 0);


  LOCK_TCPIP_CORE();


  return;


} else if (sleeptime == 0) {


  sys_check_timeouts();


  /* We try again to fetch a message from the mbox. */


  goto again;


}

如果沒有定時器sleeptime == SYS_TIMEOUTS_SLEEPTIME_INFINITE則 sys_arch_mbox_fetch(mbox, msg, 0); 參數0表示無限超時時間。

直到獲取到消息才會return,否則就一直在此等待。

這里個人覺得有個BUG,如果剛開始沒有定時器,且此時沒有消息,則在此之后新創(chuàng)建的定時器將得不到處理,因為一直在這里等待消息了,雖然一開始基本都會有定時器所以不會進到這里,但是邏輯上來說還是不嚴謹。雖然這里無限等待可以有利于效率,因為沒有消息該線程就不執(zhí)行了,但是個人覺得設置一個固定的超時間隔可能更安全,這樣保證該線程不會卡死在這里,超過時間沒有消息也跳過重新執(zhí)行,這樣保證新創(chuàng)建的定時器能執(zhí)行,最大誤差就是該設置的固定間隔。這個間隔可以根據允許誤差和效率均衡考慮設置,這樣也不至于影響效率,也能保證定時器始終能執(zhí)行。

sleeptime已經有定時器超時了sleeptime == 0則馬上調用sys_check_timeouts()處理。因為沒有消息所以goto again;重復,無需return。

如果sleeptime不是0也不是無限大,則按需設置超時時間

res = sys_arch_mbox_fetch(mbox, msg, sleeptime);

如果res返回超時則調用sys_check_timeouts處理定時器,goto again;重復上述過程,因為沒有消息所以無需return。有消息則return到上一層去處理消息。

2.7DEBUG

lwipopts.h中定義LWIP_DEBUG_TIMERNAMES宏使能相關debug代碼,

否則根據LWIP_DEBUG決定

如果定義了LWIP_DEBUG則LWIP_DEBUG_TIMERNAMES為SYS_DEBUG,否則為0。

SYS_DEBUG默認為LWIP_DBG_OFF,可以該為LWIP_DBG_ON

#ifndef LWIP_DEBUG_TIMERNAMES


#ifdef LWIP_DEBUG


#define LWIP_DEBUG_TIMERNAMES SYS_DEBUG


#else /* LWIP_DEBUG */


#define LWIP_DEBUG_TIMERNAMES 0


#endif /* LWIP_DEBUG*/


#endif

以上使能相關調試代碼之后,還需要lwipopts.h中使能

TIMERS_DEBUG

按如下配置使能

#define TIMERS_DEBUG LWIP_DBG_ON


#define LWIP_DEBUG_TIMERNAMES 1

當然也要使能DEBUG

#define LWIP_DEBUG 1

和LWIP_PLATFORM_DIAG打印的接口宏。

此時可以看到打印信息如下,可以通過打印確定定時是否正確,定時器是否工作

sct calling h=ip_reass_tmr t=0 arg=0x2001548c


tcpip: ip_reass_tmr()


sys_timeout: 0x28213e48 abs_time=6223 handler=ip_reass_tmr arg=0x2001548c


sct calling h=etharp_tmr t=0 arg=0x20015498


tcpip: etharp_tmr()


sys_timeout: 0x28213e68 abs_time=6224 handler=etharp_tmr arg=0x20015498


sct calling h=ip_reass_tmr t=0 arg=0x2001548c


tcpip: ip_reass_tmr()


sys_timeout: 0x28213e48 abs_time=7223 handler=ip_reass_tmr arg=0x2001548c


sct calling h=etharp_tmr t=0 arg=0x20015498


tcpip: etharp_tmr()


sys_timeout: 0x28213e68 abs_time=7224 handler=etharp_tmr arg=0x20015498


sct calling h=ip_reass_tmr t=0 arg=0x2001548c


tcpip: ip_reass_tmr()


sys_timeout: 0x28213e48 abs_time=8223 handler=ip_reass_tmr arg=0x2001548c


sct calling h=ip_reass_tmr t=0 arg=0x2001548c


tcpip: ip_reass_tmr()


sys_timeout: 0x28213e48 abs_time=16223 handler=ip_reass_tmr arg=0x2001548c


sct calling h=etharp_tmr t=0 arg=0x20015498


tcpip: etharp_tmr()


sys_timeout: 0x28213e68 abs_time=16224 handler=etharp_tmr arg=0x20015498

三.總結

重點理解定時器的超時判斷算法,

注意定時器是單次的每次超時處理完都會刪除,需要重新創(chuàng)建,這個需要注意,并且注意頻繁的創(chuàng)建和刪除對堆管理的影響。

了解內建定時器的定時周期的配置,以及定時器的調試方法。

審核編輯 黃宇

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 以太網
    +關注

    關注

    40

    文章

    5317

    瀏覽量

    170461
  • 定時器
    +關注

    關注

    23

    文章

    3226

    瀏覽量

    114141
  • LwIP
    +關注

    關注

    2

    文章

    85

    瀏覽量

    27018
收藏 人收藏

    評論

    相關推薦

    基于DWC_ether_qos以太網驅動開發(fā)-MAC幀格式介紹

    本文轉自公眾號,歡迎關注 基于DWC_ether_qos以太網驅動開發(fā)-MAC幀格式介紹 (qq.com) 一.前言 ? 在以太網
    的頭像 發(fā)表于 08-30 09:23 ?2070次閱讀
    基于<b class='flag-5'>DWC_ether_qos</b>的<b class='flag-5'>以太網</b><b class='flag-5'>驅動</b><b class='flag-5'>開發(fā)</b>-MAC幀格式介紹

    基于DWC_ether_qos以太網驅動開發(fā)-MDIO驅動編寫與測試

    本文轉自公眾號歡迎關注 基于DWC_ether_qos以太網驅動開發(fā)-MDIO驅動編寫與測試 一.前言
    的頭像 發(fā)表于 08-30 09:37 ?3440次閱讀
    基于<b class='flag-5'>DWC_ether_qos</b>的<b class='flag-5'>以太網</b><b class='flag-5'>驅動</b><b class='flag-5'>開發(fā)</b>-MDIO<b class='flag-5'>驅動</b>編寫與測試

    基于DWC_ether_qos以太網驅動開發(fā)-描述符鏈表介紹

    本文轉自公眾號歡迎關注 一.描述符概述 1.0 前言 對于DWC Ethernet QoS驅動的編寫來說,初始化完成之后,核心操作就是DMA的描述符鏈表配置(linked list
    的頭像 發(fā)表于 08-30 09:39 ?4149次閱讀
    基于<b class='flag-5'>DWC_ether_qos</b>的<b class='flag-5'>以太網</b><b class='flag-5'>驅動</b><b class='flag-5'>開發(fā)</b>-描述符鏈表介紹

    基于DWC_ether_qos以太網驅動開發(fā)-數據流驗證過程

    轉自公眾號歡迎關注 https://mp.weixin.qq.com/s/klrHhaLMM_0W3FGVwHXFkA 基于DWC_ether_qos以太網驅動開發(fā)-數據流驗證過程
    的頭像 發(fā)表于 08-31 08:41 ?1776次閱讀
    基于<b class='flag-5'>DWC_ether_qos</b>的<b class='flag-5'>以太網</b><b class='flag-5'>驅動</b><b class='flag-5'>開發(fā)</b>-數據流驗證過程

    基于DWC_ether_qos以太網驅動開發(fā)-收發(fā)驅動編寫與調試

    本文轉自公眾號,歡迎關注 基于DWC_ether_qos以太網驅動開發(fā)-收發(fā)驅動編寫與調試 (qq.com) https://mp.wei
    的頭像 發(fā)表于 09-05 08:47 ?2060次閱讀
    基于<b class='flag-5'>DWC_ether_qos</b>的<b class='flag-5'>以太網</b><b class='flag-5'>驅動</b><b class='flag-5'>開發(fā)</b>-收發(fā)<b class='flag-5'>驅動</b>編寫與調試

    基于DWC_ether_qos以太網驅動開發(fā)-無OS環(huán)境移植LWIP

    本文轉自公眾號歡迎關注 基于DWC_ether_qos以太網驅動開發(fā)-無OS環(huán)境移植LWIP (qq.com) https://mp.we
    的頭像 發(fā)表于 09-06 08:40 ?1417次閱讀
    基于<b class='flag-5'>DWC_ether_qos</b>的<b class='flag-5'>以太網</b><b class='flag-5'>驅動</b><b class='flag-5'>開發(fā)</b>-無OS環(huán)境移植<b class='flag-5'>LWIP</b>

    基于DWC_ether_qos以太網驅動開發(fā)-LWIP的內存池介紹

    本文轉自公眾號,歡迎關注 https://mp.weixin.qq.com/s/mBoGSf_u9edFF01U_OZT9g 一.前言 lwIP為基礎結構提供了專用的內存池管理,比如netconn
    的頭像 發(fā)表于 09-07 08:45 ?1491次閱讀
    基于<b class='flag-5'>DWC_ether_qos</b>的<b class='flag-5'>以太網</b><b class='flag-5'>驅動</b><b class='flag-5'>開發(fā)</b>-<b class='flag-5'>LWIP</b>的內存池介紹

    基于DWC_ether_qos以太網驅動開發(fā)-LWIP的堆管理介紹

    本文轉自公眾號歡迎關注 基于DWC_ether_qos以太網驅動開發(fā)-LWIP的堆管理介紹 (qq.com) https://mp.wei
    的頭像 發(fā)表于 09-08 08:40 ?1135次閱讀
    基于<b class='flag-5'>DWC_ether_qos</b>的<b class='flag-5'>以太網</b><b class='flag-5'>驅動</b><b class='flag-5'>開發(fā)</b>-<b class='flag-5'>LWIP</b>的堆管理介紹

    基于DWC_ether_qos以太網驅動開發(fā)-LWIP的堆(內存池)未對齊導致問題的案例分享

    本文轉自公眾號歡迎關注 https://mp.weixin.qq.com/s/ErIa2ss2YZLGYbSwoJEzog 一.?前言 內存未對齊訪問問題這個已經是老生常談的問題了, 由于LWIP
    的頭像 發(fā)表于 09-09 08:44 ?1595次閱讀
    基于<b class='flag-5'>DWC_ether_qos</b>的<b class='flag-5'>以太網</b><b class='flag-5'>驅動</b><b class='flag-5'>開發(fā)</b>-<b class='flag-5'>LWIP</b>的堆(內存池)未對齊導致問題的案例分享

    基于DWC_ether_qos以太網驅動開發(fā)-RTOS環(huán)境移植LWIP與性能測試

    本文轉自公眾號,歡迎關注 基于DWC_ether_qos以太網驅動開發(fā)-RTOS環(huán)境移植LWIP與性能測試 (qq.com) https:
    的頭像 發(fā)表于 09-11 11:20 ?1787次閱讀
    基于<b class='flag-5'>DWC_ether_qos</b>的<b class='flag-5'>以太網</b><b class='flag-5'>驅動</b><b class='flag-5'>開發(fā)</b>-RTOS環(huán)境移植<b class='flag-5'>LWIP</b>與性能測試

    基于DWC_ether_qos以太網驅動開發(fā)-LWIP在PC上進行開發(fā)調試

    本文轉自公眾號歡迎關注 基于DWC_ether_qos以太網驅動開發(fā)-LWIP在PC上進行開發(fā)
    的頭像 發(fā)表于 09-11 08:40 ?1798次閱讀
    基于<b class='flag-5'>DWC_ether_qos</b>的<b class='flag-5'>以太網</b><b class='flag-5'>驅動</b><b class='flag-5'>開發(fā)</b>-<b class='flag-5'>LWIP</b>在PC上進行<b class='flag-5'>開發(fā)</b>調試

    基于DWC_ether_qos以太網驅動開發(fā)-LWIP的ARP模塊介紹

    TCP/IP通訊第一步需要先調通ARP,否則TCP/IP包都不知道MAC地址要發(fā)給誰。這一篇來基于LWIP的ARP實現(xiàn)進行相關的分析。
    的頭像 發(fā)表于 09-18 09:34 ?1629次閱讀
    基于<b class='flag-5'>DWC_ether_qos</b>的<b class='flag-5'>以太網</b><b class='flag-5'>驅動</b><b class='flag-5'>開發(fā)</b>-<b class='flag-5'>LWIP</b>的ARP<b class='flag-5'>模塊</b>介紹

    以太網Lwip例程

    以太網Lwip例程
    發(fā)表于 12-06 16:53 ?25次下載
    <b class='flag-5'>以太網</b><b class='flag-5'>Lwip</b>例程

    設計軟件核心以太網服務質量數據手冊免費下載

    本文描述Synopsys設計軟件核心以太網服務質量DWC以太網QoS核心5.10A。DWC以太網
    發(fā)表于 10-23 08:00 ?16次下載
    設計軟件核心<b class='flag-5'>以太網</b>服務質量數據手冊免費下載

    基于DWC_ether_qos以太網驅動開發(fā)-包過濾

    以太網上數據非常多,如果所有數據都接收交給軟件去處理軟件負載會非常重,所以一般只需要接收發(fā)給自己的數據即可
    的頭像 發(fā)表于 09-02 09:19 ?1428次閱讀
    基于<b class='flag-5'>DWC_ether_qos</b>的<b class='flag-5'>以太網</b><b class='flag-5'>驅動</b><b class='flag-5'>開發(fā)</b>-包過濾