Lwip数据传递流程及主要函数理解


typora-copy-images-to: upload


Lwip首先需要了解三个部分:

1.内存管理 (替代C库malloc的一系列操作)

2.网络接口 与实际的PHY芯片,MAC层之间的接口,以及操作系统的函数接口

3.数据包 特指pbuf结构体,数据处理以及上下层传递均建立在pbuf的基础上

了解了这三个部分之后,便可展开源码,以下梳理出其中的一些重要的函数,不涉及具体的网络协议,大概明白了数据传递的过程。

TCPIP_Init

初始化,很大部分代码都是绑定函数。

tcpip_init

​ 创建 tcpip_thread 线程和线程所用邮箱 tcpip_mbox。在tcpip_thread 线程中根据底层传来的包类型进行不同的处理。

netif_add

​ 注册网卡,进行网卡底层初始化,绑定底层输入输出函数,绑定底层传向上层的函数netif->input,调用ethernetif_init---->low_level_init中创建ethernetif_input线程,获取中断释放的信号量s_xSemaphore,获取成功后执行low_level_input返回一个pbuf对象p,再调用 netif->input(p, netif) 。该函数指针为netif_add 传入的参数。 netif为全局变量网卡gnetif ,input函数为 tcpip_input。在tcpip_input中将ethernet_input()函数作为结构体的一部分传递给内核,然后内核接收到这个数据包就调用该函数

tcpip_input

这个函数中调用tcpip_inpkt函数,将底层传入的pbuf、netif、ethernet_input()函数打包成tcpip_msg类型的的包,将包发送至 tcpip_mbox 邮箱,在 tcpip_thread 线程中进行处理。包类型赋值固定值TCPIP_MSG_INPKT宏,表示传进来的数据包。tcpip_thread 线程中实际调用 tcpip_thread_handle_msg(msg),然后根据包类型(TCPIP_MSG_INPKT)调用 msg->msg.inp.input_fn 指针,而该指针正是在 tcpip_input 函数中指定的 ethernet_input 或 ip_input

err_t tcpip_input(struct pbuf *p, struct netif *inp)
{
#if LWIP_ETHERNET
  if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
    return tcpip_inpkt(p, inp, ethernet_input);//在这里面到宝成msg发送邮箱
  } else
#endif /* LWIP_ETHERNET */
    return tcpip_inpkt(p, inp, ip_input);
}

ip_input 就是 ip4_input 的宏。实际上,在 ethernet_input 也可能会调用 ip4_input ,用于处理IP协议。除此之外,ethernet_input 还可以处理ARP协议(调用 etharp_input),总之tcpip_thread_handle_msg就是一个case语句

tcpip_thread线程

static void tcpip_thread(void *arg)
{
  struct tcpip_msg *msg;
  LWIP_UNUSED_ARG(arg);

  LWIP_MARK_TCPIP_THREAD();

  LOCK_TCPIP_CORE();
  if (tcpip_init_done != NULL) {
    tcpip_init_done(tcpip_init_done_arg);
  }

  while (1) {                          /* MAIN Loop */
    LWIP_TCPIP_THREAD_ALIVE();
    /* wait for a message, timeouts are processed while waiting */
    TCPIP_MBOX_FETCH(&tcpip_mbox, (void **)&msg);//读邮箱
    if (msg == NULL) {
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: NULL\n"));
      LWIP_ASSERT("tcpip_thread: invalid message", 0);
      continue;
    }
    tcpip_thread_handle_msg(msg);
  }
}

真正的数据处理是 tcpip_thread_handle_msg(msg) 根据msg的类型执行对应的处理函数。

整个流程见下图
在这里插入图片描述

超时处理

两个基本结构体

struct lwip_cyclic_timer {
  u32_t interval_ms;
  lwip_cyclic_timer_handler handler;
};
struct sys_timeo {
  struct sys_timeo *next;
  u32_t time;//到达超时时刻的绝对时间
  sys_timeout_handler h;
  void *arg;
};

lwip_cyclic_timers[]

lwip_cyclic_timers[] 数组,根据宏使能定义

const struct lwip_cyclic_timer lwip_cyclic_timers[] = {
  {TCP_TMR_INTERVAL, HANDLER(tcp_tmr)},
  {IP_TMR_INTERVAL, HANDLER(ip_reass_tmr)},
  {ARP_TMR_INTERVAL, HANDLER(etharp_tmr)},
  {超时时间毫秒数,各自协议的超时回调函数},
  ...
};

sys_timeouts_init

遍历 lwip_cyclic_timers[] 数组,调用 sys_timeout 函数注册超时时间、事件函数、参数。时间为数组各元素的第一个成员interval_ms,超时函数为 lwip_cyclic_timer(*arg), 参数为 lwip_cyclic_timers[] 数组各元素的地址

void sys_timeouts_init(void)
{
  size_t i;
  for (i = (LWIP_TCP ? 1 : 0); i < LWIP_ARRAYSIZE(lwip_cyclic_timers); i++) 
  {
    sys_timeout(lwip_cyclic_timers[i].interval_ms, lwip_cyclic_timer, LWIP_CONST_CAST(void *, &lwip_cyclic_timers[i]));
  }
}

sys_timeout

本函数中,设置下一次达到超时条件的时标 next_timeout_time ,作为绝对时间传入 sys_timeout_abs 函数。实际上是调用sys_timeout_abs()函数,这里的 handle 为 lwip_cyclic_timer(void *arg)函数

void sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg)
{
  u32_t next_timeout_time;
  LWIP_ASSERT_CORE_LOCKED();
  next_timeout_time = (u32_t)(sys_now() + msecs); 
  sys_timeout_abs(next_timeout_time, handler, arg);
}

sys_timeout_abs

本函数中,处理三个 struct sys_timeo类型的结构体(两个局部变量*timeout, *t。一个全局变量next_timeout)。next_timeout是一个全局变量,初始为空链表。

static struct sys_timeo *next_timeout;//全局链表

传入的的 handle 为 lwip_cyclic_timer(void *arg)函数,传入的arg为lwip_cyclic_timers[] 数组的各个元素。使用初始化将 lwip_cyclic_timers[] 数组的每个元素的地址作为arg,与struct sys_timeo结构绑定,挂到全局链表 next_timeout 上

static void sys_timeout_abs(u32_t abs_time, sys_timeout_handler handler, void *arg)
{
  struct sys_timeo *timeout, *t;

  timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT);
  if (timeout == NULL) {
    return;
  }
  //当前设置的超时对象timeout
  timeout->next = NULL;
  timeout->h = handler;//传入的的 handle 为 lwip_cyclic_timer(void *arg)函数
  timeout->arg = arg;
  timeout->time = abs_time;//到达超时时刻的绝对时间

  if (next_timeout == NULL) { 
    next_timeout = timeout;//将结构体插入空链表
    return;
  }
  //当前超时对象与链表首部作比较
  if (TIME_LESS_THAN(timeout->time, next_timeout->time)) {//无符号数作比较,参数1是否比参数2小
    timeout->next = next_timeout;//超时时间大的排序到next
    next_timeout = timeout;//超时时间小的地址赋值给链表首地址
  } else {
    //首部不是timeout,即timeout不是最小时间的情况
    for (t = next_timeout; t != NULL; t = t->next) {//遍历链表
      if ((t->next == NULL) || TIME_LESS_THAN(timeout->time, t->next->time)) 
      {//如果当前超时对象timeout 比链表中某个对象->next 时间小
        timeout->next = t->next;
        t->next = timeout;
        break;
      }
    }
  }
}
例如按顺序插入 1 3 2
1. next_timeout为空 ,1插到首部
2. 插入3,满足条件1的next==NULL,NULL赋值给3的next,将3变为1的next.此时链表为“1 3 空”
3. 插入2,满足条件21的next的时间(3)小,将1的下一个(3)变为2的下一个,将2变为1的下一个。此时链表为“1 2 3 空”

lwip_cyclic_time函数

每个sys_timeo结构体中的arg成员变量记录着lwip_cyclic_timers【】数组各元素,在初始化的时候将他们注册到 lwip_cyclic_timer()函数中,每次在处理回调函数之后,就调用sys_timeout_abs()函数将其重新注册到超时链表中

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;
  cyclic->handler();//这里的handler为数组元素各自协议的超时回调函数
  now = sys_now();
  //重新设置下一次超时的绝对时间,时间升序排列
  next_timeout_time = (u32_t)(current_timeout_due_time + cyclic->interval_ms);
  if (TIME_LESS_THAN(next_timeout_time, now)) {
    sys_timeout_abs((u32_t)(now + cyclic->interval_ms), lwip_cyclic_timer, arg);
  } else {
    sys_timeout_abs(next_timeout_time, lwip_cyclic_timer, arg);
  }
}

超时检查

在 tcpip_thread 线程中,读取邮箱

TCPIP_MBOX_FETCH(&tcpip_mbox, (void **)&msg);//读邮箱

TCPIP_MBOX_FETCH是一个宏,真正调用的是 tcpip_timeouts_mbox_fetch

static void tcpip_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg)
{
  u32_t sleeptime, res;
again:
  LWIP_ASSERT_CORE_LOCKED();
  sleeptime = sys_timeouts_sleeptime();//得到距离事件超时的时间并保存在sleeptime变量中
  if (sleeptime == SYS_TIMEOUTS_SLEEPTIME_INFINITE)//当前无超时事件,sleeptime为ffffffff,那只需一直等邮箱
  {
    UNLOCK_TCPIP_CORE();
    sys_arch_mbox_fetch(mbox, msg, 0);//0表示无需等别的事,立即查邮箱
    LOCK_TCPIP_CORE();
    return;
  } else if (sleeptime == 0) {//如果sleeptime为0表示发生超时了,那就调用sys_check_timeouts()去检查一下到底是哪个事件发生超时并且去处理其超时回调函数
    sys_check_timeouts();
    goto again;
  }
  //有超时事件但是时间未到
  UNLOCK_TCPIP_CORE();
  //等待tcpip_mbox的消息的同时就去处理超时事件
  //如果接收到消息,并且超时时间还没到,那就去处理tcpip_mbox的消息,然后再回来重新计算等待时间sleeptime
  res = sys_arch_mbox_fetch(mbox, msg, sleeptime);//返回等待时间
  LOCK_TCPIP_CORE();
  if (res == SYS_ARCH_TIMEOUT) {//ffffffff
    sys_check_timeouts();
    goto again;
  }
}

发生超时时实际上是调用 sys_check_timeouts ,看看是哪一个超时

void sys_check_timeouts(void)
{
  u32_t now;
  LWIP_ASSERT_CORE_LOCKED();
  now = sys_now();
  do {
    struct sys_timeo *tmptimeout;
    sys_timeout_handler handler;
    void *arg;
    PBUF_CHECK_FREE_OOSEQ();
    tmptimeout = next_timeout;//由于链表是从小到大时间排序,因此赋值为链表首部
    if (tmptimeout == NULL) {
      return;
    }if (TIME_LESS_THAN(now, tmptimeout->time)) {return;//还没到时间,则退出}

​    next_timeout = tmptimeout->next;
​    handler = tmptimeout->h;//这里的handler为lwip_cyclic_timer函数
​    arg = tmptimeout->arg;//arg则是那个数组元素
​    current_timeout_due_time = tmptimeout->time;memp_free(MEMP_SYS_TIMEOUT, tmptimeout);if (handler != NULL) {handler(arg);//调用超时回调函数 lwip_cyclic_timer(void *arg),在内部调用真正的超时函数,然后调整超时链表}LWIP_TCPIP_THREAD_ALIVE();
  } while (1);
}
  • 4
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值