1.lwIP超时事件简介
为每个与外界网络连接的任务都设定了timeout属性,即等待超时时间,例如TCP建立连接超时、ARP缓存表项的时间管理等,都需要超时操作来处理。
超时事件是以链表的形式链接起来,形成了单向链表。
2.lwIP超时事件机制
超时事件管理
lwip_cyclic_timer是轮询超时事件结构体,包含超时事件interval_ms以及超时处理函数lwip_cyclic_timer_handler handler,lwip_cyclic_timer_handler是定义的结构体;
sys_timeo是管理超时事件的结构体,其中有next指针指向下一个超时事件的指针;time代表当前超时事件的等待时间;sys_timeout_handler h指向超时的回调函数;*arg是超时回调函数的形参;
其中sys_timeout_handler是定义的结构体,参数是void *arg。
超时事件注册
const struct lwip_cyclic_timer lwip_cyclic_timers[]定义了超时的数组,根据上面所讲的,里面包含的是interval的超时事件,以及对应的超时事件;这其中会通过宏定义的方式来进行这两个参数的定义;
sys_timeouts_init来进行超时的初始化,其中会通过for循环来查询所有的定义过得超时事件;通过sys_timeout来进行处理,其中有超时时间,超时回调函数以及lwip_cyclic_timers[i]的地址;sys_timeout会在系统节拍的基础上,加上超时时间得到下一次的超时时间;sys_timeout中,计算得到next_timeout_time(即下一次的超时时间),就会调用sys_timeout_abs函数;该函数中会申请一个sys_timeo的结构体,然后对这个结构体进行初始化;
初始化完之后,对单向链表进行操作:首先插入表头,然后按照每一个超时时间进行排序并插入单链表中,最终顺序是从小到大进行排列。
以上操作的源码如下:
static void
#if LWIP_DEBUG_TIMERNAMES
sys_timeout_abs(u32_t abs_time, sys_timeout_handler handler, void *arg, const char *handler_name)
#else /* LWIP_DEBUG_TIMERNAMES */
sys_timeout_abs(u32_t abs_time, sys_timeout_handler handler, void *arg)
#endif
{
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 = abs_time;
#if LWIP_DEBUG_TIMERNAMES
timeout->handler_name = handler_name;
LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p abs_time=%"U32_F" handler=%s arg=%p\n",
(void *)timeout, abs_time, handler_name, (void *)arg));
#endif /* LWIP_DEBUG_TIMERNAMES */
if (next_timeout == NULL) {
next_timeout = timeout;
return;
}
if (TIME_LESS_THAN(timeout->time, next_timeout->time)) {
timeout->next = next_timeout;
next_timeout = timeout;
} else {
for (t = next_timeout; t != NULL; t = t->next) {
if ((t->next == NULL) || TIME_LESS_THAN(timeout->time, t->next->time)) {
timeout->next = t->next;
t->next = timeout;
break;
}
}
}
}
/**
* Timer callback function that calls cyclic->handler() and reschedules itself.
*
* @param arg unused argument
*/
#if !LWIP_TESTMODE
static
#endif
void
lwip_cyclic_timer(void *arg)
{
u32_t now;
u32_t next_timeout_time;
const struct lwip_cyclic_timer *cyclic = (const struct lwip_cyclic_timer *)arg;
#if LWIP_DEBUG_TIMERNAMES
LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: %s()\n", cyclic->handler_name));
#endif
cyclic->handler();
now = sys_now();
next_timeout_time = (u32_t)(current_timeout_due_time + cyclic->interval_ms); /* overflow handled by TIME_LESS_THAN macro */
if (TIME_LESS_THAN(next_timeout_time, now)) {
/* timer would immediately expire again -> "overload" -> restart without any correction */
#if LWIP_DEBUG_TIMERNAMES
sys_timeout_abs((u32_t)(now + cyclic->interval_ms), lwip_cyclic_timer, arg, cyclic->handler_name);
#else
sys_timeout_abs((u32_t)(now + cyclic->interval_ms), lwip_cyclic_timer, arg);
#endif
} else {
/* correct cyclic interval with handler execution delay and sys_check_timeouts jitter */
#if LWIP_DEBUG_TIMERNAMES
sys_timeout_abs(next_timeout_time, lwip_cyclic_timer, arg, cyclic->handler_name);
#else
sys_timeout_abs(next_timeout_time, lwip_cyclic_timer, arg);
#endif
}
}
/** Initialize this module */
void sys_timeouts_init(void)
{
size_t i;
/* tcp_tmr() at index 0 is started on demand */
for (i = (LWIP_TCP ? 1 : 0); i < LWIP_ARRAYSIZE(lwip_cyclic_timers); i++) {
/* we have to cast via size_t to get rid of const warning
(this is OK as cyclic_timer() casts back to const* */
sys_timeout(lwip_cyclic_timers[i].interval_ms, lwip_cyclic_timer, LWIP_CONST_CAST(void *, &lwip_cyclic_timers[i]));
}
}
超时事件删除
需要传入超时事件的回调函数以及其参数,然后定义两个sys_timeo的结构体prev_t以及t,通过链表的遍历找到待删除的超时事件,然后进行链表删除并通过memp_free释放内存。
超时事件查询
裸机和FreeRTOS的操作并不相通,一个是死循环,一个则是通过线程进行调用。
最终的操作都是先检查是否超时,若超时则会释放内存,同时回调函数执行相应的参数。
总结
这一章节内容不多,主要讲述了超时的一些操作,带着看了看源码。需要知道的是不是通过间隔来进行操作,都是按照绝对时刻来进行超时的判定,并通过回调函数执行对应的超时操作。