LwIP 代码分析(tcpip_init分析) – 第十三集

/ 0评 / 3

tcpip_init就是LWIP的初始化函数.一般传递tcpip_init( NULL, NULL ),也就是没有用户的init函数.

/**
 * @ingroup lwip_os
 * Initialize this module:
 * - initialize all sub modules
 * - start the tcpip_thread
 *
 * @param initfunc a function to call when tcpip_thread is running and finished initializing
 * @param arg argument to pass to initfunc
 */
void
tcpip_init(tcpip_init_done_fn initfunc, void *arg)
{
  lwip_init();
  tcpip_init_done = initfunc;
  tcpip_init_done_arg = arg;
  if (sys_mbox_new(&mbox, TCPIP_MBOX_SIZE) != ERR_OK) { /* 1)申请一个mbox */
    LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
  }
#if LWIP_TCPIP_CORE_LOCKING
  if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) { /* 2)申请一个线程总锁 */
    LWIP_ASSERT("failed to create lock_tcpip_core", 0);
  }
#endif /* LWIP_TCPIP_CORE_LOCKING */
	/* 真正任务处理.实际上数据包input解锁他. */
  sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
}

是其中因为没有传递inifunc,也没arg,所以前面init_done和init_done_arg两句就没用的.如果给他传递了,会在tcpip_thread里面先执行init_done的钩子函数.属于初始化后的结果.
而最关键的lwip_init,有很多操作其实也是没用的(非关键)

/**
 * @ingroup lwip_nosys
 * Initialize all modules.
 * Use this in NO_SYS mode. Use tcpip_init() otherwise.
 */
void
lwip_init(void)
{
#ifndef LWIP_SKIP_CONST_CHECK
  int a = 0;
  LWIP_UNUSED_ARG(a);
  LWIP_ASSERT("LWIP_CONST_CAST not implemented correctly. Check your lwIP port.", LWIP_CONST_CAST(void*, &a) == &a);
#endif
#ifndef LWIP_SKIP_PACKING_CHECK
  LWIP_ASSERT("Struct packing not implemented correctly. Check your lwIP port.", sizeof(struct packed_struct_test) == PACKED_STRUCT_TEST_EXPECTED_SIZE);
#endif
  /* Modules initialization */
  stats_init();
#if !NO_SYS
  sys_init();
#endif /* !NO_SYS */
  mem_init();
  memp_init();
  pbuf_init();
  netif_init();
#if LWIP_IPV4
  ip_init();
#if LWIP_ARP
  etharp_init();
#endif /* LWIP_ARP */
#endif /* LWIP_IPV4 */
#if LWIP_RAW
  raw_init();
#endif /* LWIP_RAW */
#if LWIP_UDP
  udp_init();
#endif /* LWIP_UDP */
#if LWIP_TCP
  tcp_init();
#endif /* LWIP_TCP */
#if LWIP_IGMP
  igmp_init();
#endif /* LWIP_IGMP */
#if LWIP_DNS
  dns_init();
#endif /* LWIP_DNS */
#if PPP_SUPPORT
  ppp_init();
#endif
#if LWIP_TIMERS
  sys_timeouts_init();
#endif /* LWIP_TIMERS */
}

实际上做过事情的代码:

void
lwip_init(void)
{
  sys_init(); /* 初始化系统的锁 */
  mem_init(); /* 初始化内存指针 */
  memp_init(); /* 初始化内存池 */
  sys_timeouts_init(); /* 定时任务 */
}

sys_init初始化系统锁,SYS_ARCH_UNPROTECT和SYS_ARCH_PROTECT用的就是这个锁,基本等于最底层的锁.
mem_init初始化了ram和ram_end定义,一般来说,ram只能在ram到ram_end - 1区申请,因为ram_end总是used,如果ram_end一使用,就溢出,他是最后一个元素+1,永远不能用.

memp_init就是循环遍历所有的mem_pool,然后用memp_init_pool来初始化.其中还分两种方法,一种是malloc方法,使用系统的RAM,一种是使用它自己的RAM.

/**
 * Initialize custom memory pool.
 * Related functions: memp_malloc_pool, memp_free_pool
 *
 * @param desc pool to initialize
 */
void
memp_init_pool(const struct memp_desc *desc)
{
#if MEMP_MEM_MALLOC
  LWIP_UNUSED_ARG(desc);
#else
  int i;
  struct memp *memp;
  *desc->tab = NULL;
  memp = (struct memp*)LWIP_MEM_ALIGN(desc->base);
  /* create a linked list of memp elements */
  for (i = 0; i < desc->num; ++i) {
    memp->next = *desc->tab;
    *desc->tab = memp;
#if MEMP_OVERFLOW_CHECK
    memp_overflow_init_element(memp, desc);
#endif /* MEMP_OVERFLOW_CHECK */
   /* cast through void* to get rid of alignment warnings */
   memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + desc->size
#if MEMP_OVERFLOW_CHECK
      + MEMP_SANITY_REGION_AFTER_ALIGNED
#endif
    );
  }
#if MEMP_STATS
  desc->stats->avail = desc->num;
#endif /* MEMP_STATS */
#endif /* !MEMP_MEM_MALLOC */
#if MEMP_STATS && (defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY)
  desc->stats->name  = desc->desc;
#endif /* MEMP_STATS && (defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY) */
}

可见,根据desc->size + MEMP_SIZE(不检查溢出时为0),赋值memp所在地址.

所以实际上有用代码只有这么点.

  for (i = 0; i < desc->num; ++i) {
    memp->next = *desc->tab;
    *desc->tab = memp;
   /* cast through void* to get rid of alignment warnings */
   memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + desc->size
    );
  }

那大小哪里定义呢:

他们都是一一对应的.

总体来说他的初始化就是个赋值操作.
最后,sys_timeouts_init就涉及定时任务了.

/** 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, cyclic_timer, LWIP_CONST_CAST(void*, &lwip_cyclic_timers[i]));
  }
  /* Initialise timestamp for sys_check_timeouts */
  timeouts_last_time = sys_now();
}

看出来,最主要就是循环sys_timeout,而这个正是定时任务,网络应用里面很多定时应用.最关键肯定是lwip_cyclic_timer,第二个参数对应的是执行用的fn,然后是参数.
我们看看需要定时的任务有很多.

/** This array contains all stack-internal cyclic timers. To get the number of
 * timers, use LWIP_ARRAYSIZE() */
const struct lwip_cyclic_timer lwip_cyclic_timers[] = {
#if LWIP_TCP
  /* The TCP timer is a special case: it does not have to run always and
     is triggered to start from TCP using tcp_timer_needed() */
  {TCP_TMR_INTERVAL, HANDLER(tcp_tmr)},
#endif /* LWIP_TCP */
#if LWIP_IPV4
#if IP_REASSEMBLY
  {IP_TMR_INTERVAL, HANDLER(ip_reass_tmr)},
#endif /* IP_REASSEMBLY */
#if LWIP_ARP
  {ARP_TMR_INTERVAL, HANDLER(etharp_tmr)},
#endif /* LWIP_ARP */
#if LWIP_DHCP
  {DHCP_COARSE_TIMER_MSECS, HANDLER(dhcp_coarse_tmr)},
  {DHCP_FINE_TIMER_MSECS, HANDLER(dhcp_fine_tmr)},
#endif /* LWIP_DHCP */
#if LWIP_AUTOIP
  {AUTOIP_TMR_INTERVAL, HANDLER(autoip_tmr)},
#endif /* LWIP_AUTOIP */
#if LWIP_IGMP
  {IGMP_TMR_INTERVAL, HANDLER(igmp_tmr)},
#endif /* LWIP_IGMP */
#endif /* LWIP_IPV4 */
#if LWIP_DNS
  {DNS_TMR_INTERVAL, HANDLER(dns_tmr)},
#endif /* LWIP_DNS */
#if LWIP_IPV6
  {ND6_TMR_INTERVAL, HANDLER(nd6_tmr)},
#if LWIP_IPV6_REASS
  {IP6_REASS_TMR_INTERVAL, HANDLER(ip6_reass_tmr)},
#endif /* LWIP_IPV6_REASS */
#if LWIP_IPV6_MLD
  {MLD6_TMR_INTERVAL, HANDLER(mld6_tmr)},
#endif /* LWIP_IPV6_MLD */
#endif /* LWIP_IPV6 */
};

比如tcp_tmr,在定时器内做的肯定是关于TCP的定时器.TCP比如断开连接等等,都是需要定时器参与的.就知道这一点,足够了.
接下来的初始化,就是之前说过的内容了.回顾下.

/**
  *  初始化总入口
  */
void MX_LWIP_Init(void)
{
    /* 初始化协议栈,其实就是申请线程申请锁. */
    tcpip_init( NULL, NULL );
    /* 默认清零地址(DHCP方式申请) */
    ipaddr.addr = 0;
    netmask.addr = 0;
    gw.addr = 0;
    /* 增加一个网络接口,ethernetif_init是底层初始化,tcpip_input是协议栈入口,这个有点复杂.在ethernetif.c中调用ethernetif_input,这个来调用netif->input,最后引用了tcpip_input函数. */
    netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &tcpip_input);
    /* 注册默认网络接口. */
    netif_set_default(&gnetif);
    if (netif_is_link_up(&gnetif))
    {
        /* 如果网络是连上,就介入网络好了. */
        netif_set_up(&gnetif);
    }
    else
    {
        /* 网络没连上就关闭网络. */
        netif_set_down(&gnetif);
    }
    /* DHCP其实完全可以开一条线程来守护,不然只能先插网线后上电. */
    dhcp_start(&gnetif);
}

 

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注