正点原子lwIP学习笔记——lwIP超时事件处理

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的操作并不相通,一个是死循环,一个则是通过线程进行调用。
最终的操作都是先检查是否超时,若超时则会释放内存,同时回调函数执行相应的参数。

总结

这一章节内容不多,主要讲述了超时的一些操作,带着看了看源码。需要知道的是不是通过间隔来进行操作,都是按照绝对时刻来进行超时的判定,并通过回调函数执行对应的超时操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值