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, ðernetif_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); }