LWIP_简记(8.tcpip_init()之lwip_init())

LWIP一句话记住就行:
一项工程,两份配置,三种内存分配,四套操作API,五步初始化,六个"数据流",七个数据结构
-------------------------------------------

从这一讲开始我们来剖析一下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();
  lwip_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(); /* 定时任务 */
}

少了很多,清楚明了,但是条件编译有些还是需要分析的,特别是一些协议的初始化.那么不多说,我们还是逐一来分析吧.

关于LWIP_SKIP_CONST_CHECK没什么解释的,看到check一般就顾名思义吧,应该是做一些检查.
1.stats_init()

void
stats_init(void)
{
#ifdef LWIP_DEBUG
#if MEM_STATS                                                                                                                                                          
  lwip_stats.mem.name = "MEM";
#endif /* MEM_STATS */
#endif /* LWIP_DEBUG */
}

好像也没有做什么重要的事情,就是赋值动作.lwip_stats结构体还是很复杂的,进去一看发现各种state的结构体,放在这里吧,不知道有啥用.待进一步研究.
struct stats_ lwip_stats;

/** lwIP stats container */
struct stats_ {
#if LINK_STATS
  /** Link level */
  struct stats_proto link;
#endif
#if ETHARP_STATS
  /** ARP */
  struct stats_proto etharp;
#endif
#if IPFRAG_STATS
  /** Fragmentation */
  struct stats_proto ip_frag;
#endif
#if IP_STATS
  /** IP */
  struct stats_proto ip;
#endif
#if ICMP_STATS
  /** ICMP */                                                                                                                                                          
  struct stats_proto icmp;
#endif
#if IGMP_STATS
  /** IGMP */
  struct stats_igmp igmp;
#endif
#if UDP_STATS
  /** UDP */
  struct stats_proto udp;
#endif                                                                                                                                                                 
#if TCP_STATS
  /** TCP */
  struct stats_proto tcp;
#endif
#if MEM_STATS
  /** Heap */
  struct stats_mem mem;
#endif
#if MEMP_STATS
  /** Internal memory pools */
  struct stats_mem *memp[MEMP_MAX];
#endif
#if SYS_STATS
  /** System */
  struct stats_sys sys;
#endif
#if IP6_STATS
  /** IPv6 */
  struct stats_proto ip6;
#endif
#if ICMP6_STATS
  /** ICMP6 */
  struct stats_proto icmp6;
#endif
#if IP6_FRAG_STATS
  /** IPv6 fragmentation */
  struct stats_proto ip6_frag;
#endif
#if MLD6_STATS
  /** Multicast listener discovery */
  struct stats_igmp mld6;
#endif
#if ND6_STATS
  /** Neighbor discovery */
  struct stats_proto nd6;
#endif
#if MIB2_STATS
  /** SNMP MIB2 */
  struct stats_mib2 mib2;
#endif
};

回到lwip_init()主线上来.

2.sys_init()
受NO_SYS宏控制,目前是个空函数,暂时也不管.

3.lwip_mem_init()

/**
 * Zero the heap and initialize start, end and lowest-free
 */
void                                                                                                                                                                   
lwip_mem_init(void)
{
  struct mem *mem;
 
  LWIP_ASSERT("Sanity check alignment",
              (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT - 1)) == 0);
 
  /* align the heap */
  ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER);
  /* initialize the start of the heap */
  mem = (struct mem *)(void *)ram;
  mem->next = MEM_SIZE_ALIGNED;
  mem->prev = 0;
  mem->used = 0;
  /* initialize the end of the heap */
  ram_end = ptr_to_mem(MEM_SIZE_ALIGNED);
  ram_end->used = 1;
  ram_end->next = MEM_SIZE_ALIGNED;
  ram_end->prev = MEM_SIZE_ALIGNED;
  MEM_SANITY();
 
  /* initialize the lowest-free pointer to the start of the heap */
  lfree = (struct mem *)(void *)ram;
 
  MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED);
 
  if (sys_mutex_new(&mem_mutex) != ERR_OK) {
    LWIP_ASSERT("failed to create mem_mutex", 0);
  }
}

内存堆的初始化,初始化了ram和ram_end定义,一般来说,ram只能在ram到ram_end – 1区申请,因为ram_end总是used,如果ram_end一使用,就溢出,他是最后一个元素+1,永远不能用.
3.1定义一个mem结构体
显然是一个双向链表.

/**
 * The heap is made up as a list of structs of this type.
 * This does not have to be aligned since for getting its size,
 * we only use the macro SIZEOF_STRUCT_MEM, which automatically aligns.
 */
struct mem {
  /** index (-> ram[next]) of the next struct */
  mem_size_t next;
  /** index (-> ram[prev]) of the previous struct */
  mem_size_t prev;
  /** 1: this area is used; 0: this area is unused */
  u8_t used;
#if MEM_OVERFLOW_CHECK
  /** this keeps track of the user allocation size for guard checks */
  mem_size_t user_size;
#endif
};

3.2对齐操作
ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER);
3.3初始化堆的start和end

4.memp_init()

/**
 * Initializes lwIP built-in pools.
 * Related functions: memp_malloc, memp_free
 *
 * Carves out memp_memory into linked lists for each pool-type.
 */
void
memp_init(void)
{
  u16_t i;
 
  /* for every pool: */
  for (i = 0; i < LWIP_ARRAYSIZE(memp_pools); i++) {
    memp_init_pool(memp_pools[i]);
 
#if LWIP_STATS && MEMP_STATS
    lwip_stats.memp[i] = memp_pools[i]->stats;
#endif
  }
 
#if MEMP_OVERFLOW_CHECK >= 2
  /* check everything a first time to see if it worked */
  memp_overflow_check_all();
#endif /* MEMP_OVERFLOW_CHECK >= 2 */
}                  

内存池初始化,这个就不展开讲了,回去看看第四讲,提示一下name,type,size,desc四个主要元素.能想起来吗?

5.pbuf_init()
目前也是空函数

6.netif_init()
重要的东西来了,前面也应提到过了,本来计划单独拿出来的.但是放在这里还是系统些.虽然本篇篇幅会相对较长.

void
netif_init(void)
{
#if LWIP_HAVE_LOOPIF
#if LWIP_IPV4
#define LOOPIF_ADDRINIT &loop_ipaddr, &loop_netmask, &loop_gw,
  ip4_addr_t loop_ipaddr, loop_netmask, loop_gw;
  IP4_ADDR(&loop_gw, 127, 0, 0, 1);
  IP4_ADDR(&loop_ipaddr, 127, 0, 0, 1);
  IP4_ADDR(&loop_netmask, 255, 0, 0, 0);
#else /* LWIP_IPV4 */
#define LOOPIF_ADDRINIT
#endif /* LWIP_IPV4 */
 
#if NO_SYS
  netif_add(&loop_netif, LOOPIF_ADDRINIT NULL, netif_loopif_init, ip_input);
#else  /* NO_SYS */
  netif_add(&loop_netif, LOOPIF_ADDRINIT NULL, netif_loopif_init, tcpip_input);
#endif /* NO_SYS */
 
#if LWIP_IPV6
  IP_ADDR6_HOST(loop_netif.ip6_addr, 0, 0, 0, 0x00000001UL);
  loop_netif.ip6_addr_state[0] = IP6_ADDR_VALID;
#endif /* LWIP_IPV6 */
 
  netif_set_link_up(&loop_netif);
  netif_set_up(&loop_netif);
 
#endif /* LWIP_HAVE_LOOPIF */
}

6.1利用ip4_ADDR()函数设置默认网关,隐码,ip地址(就是回环地址).
6.2利用netif_add()函数添加回环接口到netif链表中.
我们以有OS的为例
netif_add(&loop_netif, LOOPIF_ADDRINIT NULL, netif_loopif_init, tcpip_input);
netif_add函数的原型太过庞大,这里我只分析一下参数

struct netif *netif_add(struct netif *netif,const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,void *state, netif_init_fn init, netif_input_fn input)

参数:添加的网卡接口,ip地址,隐码,网关,状态,网卡接口初始化回调函数,网卡数据接收回调函数
关于网卡接口也就是netif结构体,这里也需要展开来讲:

/** Generic data structure used for all lwIP network interfaces.
 *  The following fields should be filled in by the initialization
 *  function for the device driver: hwaddr_len, hwaddr[], mtu, flags */
struct netif {
#if !LWIP_SINGLE_NETIF
  /** pointer to next in linked list */
  struct netif *next;
#endif
 
#if LWIP_IPV4
  /** IP address configuration in network byte order */
  ip_addr_t ip_addr;
  ip_addr_t netmask;
  ip_addr_t gw;
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
  /** Array of IPv6 addresses for this netif. */
  ip_addr_t ip6_addr[LWIP_IPV6_NUM_ADDRESSES];
  /** The state of each IPv6 address (Tentative, Preferred, etc).
   * @see ip6_addr.h */
  u8_t ip6_addr_state[LWIP_IPV6_NUM_ADDRESSES];
#if LWIP_IPV6_ADDRESS_LIFETIMES
  /** Remaining valid and preferred lifetime of each IPv6 address, in seconds.
   * For valid lifetimes, the special value of IP6_ADDR_LIFE_STATIC (0)
   * indicates the address is static and has no lifetimes. */
  u32_t ip6_addr_valid_life[LWIP_IPV6_NUM_ADDRESSES];
  u32_t ip6_addr_pref_life[LWIP_IPV6_NUM_ADDRESSES];
#endif /* LWIP_IPV6_ADDRESS_LIFETIMES */
#endif /* LWIP_IPV6 */
 /** This function is called by the network device driver
   *  to pass a packet up the TCP/IP stack. */
  netif_input_fn input;
#if LWIP_IPV4
  /** This function is called by the IP module when it wants
   *  to send a packet on the interface. This function typically
   *  first resolves the hardware address, then sends the packet.
   *  For ethernet physical layer, this is usually etharp_output() */
  netif_output_fn output;
#endif /* LWIP_IPV4 */
  /** This function is called by ethernet_output() when it wants
   *  to send a packet on the interface. This function outputs
   *  the pbuf as-is on the link medium. */
  netif_linkoutput_fn linkoutput;
#if LWIP_IPV6
  /** This function is called by the IPv6 module when it wants
   *  to send a packet on the interface. This function typically
   *  first resolves the hardware address, then sends the packet.
   *  For ethernet physical layer, this is usually ethip6_output() */
  netif_output_ip6_fn output_ip6;
#endif /* LWIP_IPV6 */
#if LWIP_NETIF_STATUS_CALLBACK
  /** This function is called when the netif state is set to up or down
   */
  netif_status_callback_fn status_callback;
#endif /* LWIP_NETIF_STATUS_CALLBACK */                                                                                                                                
#if LWIP_NETIF_LINK_CALLBACK
  /** This function is called when the netif link is set to up or down
   */
  netif_status_callback_fn link_callback;
#endif /* LWIP_NETIF_LINK_CALLBACK */
#if LWIP_NETIF_REMOVE_CALLBACK
  /** This function is called when the netif has been removed */
  netif_status_callback_fn remove_callback;
#endif /* LWIP_NETIF_REMOVE_CALLBACK */
  /** This field can be set by the device driver and could point
   *  to state information for the device. */
  void *state;
#ifdef netif_get_client_data
  void* client_data[LWIP_NETIF_CLIENT_DATA_INDEX_MAX + LWIP_NUM_NETIF_CLIENT_DATA];
#endif
#if LWIP_NETIF_HOSTNAME
  /* the hostname for this netif, NULL is a valid value */
  const char*  hostname;
#endif /* LWIP_NETIF_HOSTNAME */
#if LWIP_CHECKSUM_CTRL_PER_NETIF
  u16_t chksum_flags;
#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/
  /** maximum transfer unit (in bytes) */
  u16_t mtu;
#if LWIP_IPV6 && LWIP_ND6_ALLOW_RA_UPDATES                                                                                                                             
  /** maximum transfer unit (in bytes), updated by RA */
  u16_t mtu6;
#endif /* LWIP_IPV6 && LWIP_ND6_ALLOW_RA_UPDATES */
  /** link level hardware address of this interface */
  u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
  /** number of bytes used in hwaddr */
  u8_t hwaddr_len;
  /** flags (@see @ref netif_flags) */
  u8_t flags;
  /** descriptive abbreviation */                                                                                                                                      
  char name[2];
  /** number of this interface. Used for @ref if_api and @ref netifapi_netif, 
   * as well as for IPv6 zones */
  u8_t num;
#if LWIP_IPV6_AUTOCONFIG
  /** is this netif enabled for IPv6 autoconfiguration */
  u8_t ip6_autoconfig_enabled;
#endif /* LWIP_IPV6_AUTOCONFIG */
#if LWIP_IPV6_SEND_ROUTER_SOLICIT
  /** Number of Router Solicitation messages that remain to be sent. */
  u8_t rs_count;
#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
#if MIB2_STATS
  /** link type (from "snmp_ifType" enum from snmp_mib2.h) */
  u8_t link_type;
  /** (estimate) link speed */
  u32_t link_speed;
  /** timestamp at last change made (up/down) */
  u32_t ts;
  /** counters */
  struct stats_mib2_netif_ctrs mib2_counters;
#endif /* MIB2_STATS */
#if LWIP_IPV4 && LWIP_IGMP
  /** This function could be called to add or delete an entry in the multicast
      filter table of the ethernet MAC.*/
  netif_igmp_mac_filter_fn igmp_mac_filter;
#endif /* LWIP_IPV4 && LWIP_IGMP */
#if LWIP_IPV6 && LWIP_IPV6_MLD
  /** This function could be called to add or delete an entry in the IPv6 multicast
      filter table of the ethernet MAC. */
  netif_mld_mac_filter_fn mld_mac_filter;
#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
#if LWIP_NETIF_USE_HINTS
  struct netif_hint *hints;
#endif /* LWIP_NETIF_USE_HINTS */
#if ENABLE_LOOPBACK
  /* List of packets to be queued for ourselves. */
  struct pbuf *loop_first;
  struct pbuf *loop_last;
#if LWIP_LOOPBACK_MAX_PBUFS
  u16_t loop_cnt_current;
#endif /* LWIP_LOOPBACK_MAX_PBUFS */
#endif /* ENABLE_LOOPBACK */                                                                                                                                           
};

有没有被吓到,反正我是被吓到了.所以做一下减法:
条件编译和ipv6的先不看

struct netif {
	struct netif *next; //显然可以存在多网卡组成单向链表.
#if LWIP_IPV4
	ip_addr_t ip_addr;
   ip_addr_t netmask;
   ip_addr_t gw;
#endif /* LWIP_IPV4 */
   netif_input_fn input; //网卡数据接收回调函数,面向ip协议
   netif_output_fn output; //网卡数据发送回调函数
   netif_linkoutput_fn linkoutput; //网卡数据接收回调函数,(我理解为面向arp协议)
   netif_status_callback_fn status_callback; //网卡启动/关闭回调函数
   u16_t mtu;   //最大数据传输单元
   const char*  hostname;  //主机名
   u8_t hwaddr[NETIF_MAX_HWADDR_LEN];  //mac地址,NETIF_MAX_HWADDR_LEN猜到应该是6
   u8_t hwaddr_len;  //mac地址长度
   u8_t flags; 
   u8_t num;  //网卡被用次数
    ...
}

这样一看就简单多了,结合数据传输理解也很容易想到这些成员变量.
6.3启动接口
netif_set_link_up(&loop_netif);
netif_set_up(&loop_netif);
netif_set_up是使能网卡,设置NETIF_FLAG_UP标志位,必须在网卡被使用前用户来调用
netif_set_link_up是当网卡链路层active时由网卡驱动来设置的,如,station关联上AP后就应该调用netif_set_link_up

7.ip_init()/etharp_init()/raw_init()
都是空函数

8.udp_init()/tcp_init()
这两个函数也简单,
udp_port = UDP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND());
tcp_port = TCP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND());
就做了一件事,限制端口号的范围

9.igmp_init()

/**
 * Initialize the IGMP module
 */
void
igmp_init(void)                                                                                                                                                        
{
  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n"));
  IP4_ADDR(&allsystems, 224, 0, 0, 1);
  IP4_ADDR(&allrouters, 224, 0, 0, 2);
}

10.dns_init()
这个也需要详细讲解一下

/**
 * Initialize the resolver: set up the UDP pcb and configure the default server
 * (if DNS_SERVER_ADDRESS is set).
 */
void
dns_init(void)
{
#ifdef DNS_SERVER_ADDRESS
  /* initialize default DNS server address */
  ip_addr_t dnsserver;
  DNS_SERVER_ADDRESS(&dnsserver);
  dns_setserver(0, &dnsserver);
#endif /* DNS_SERVER_ADDRESS */
 
  LWIP_ASSERT("sanity check SIZEOF_DNS_QUERY",
              sizeof(struct dns_query) == SIZEOF_DNS_QUERY);
  LWIP_ASSERT("sanity check SIZEOF_DNS_ANSWER",
              sizeof(struct dns_answer) <= SIZEOF_DNS_ANSWER_ASSERT);
 
  LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
                                                                                                                                                                       
  /* if dns client not yet initialized... */
#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0)
  if (dns_pcbs[0] == NULL) {
    dns_pcbs[0] = udp_new_ip_type(IPADDR_TYPE_ANY);
    LWIP_ASSERT("dns_pcbs[0] != NULL", dns_pcbs[0] != NULL);
 
    /* initialize DNS table not needed (initialized to zero since it is a
     * global variable) */
    LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0",
                DNS_STATE_UNUSED == 0);
 
    /* initialize DNS client */
    udp_bind(dns_pcbs[0], IP_ANY_TYPE, 0);
    udp_recv(dns_pcbs[0], dns_recv, NULL);
  }
#endif
 
#if DNS_LOCAL_HOSTLIST
  dns_init_local();
#endif                                                                                                                                                                 
}

10.1设置默认的dns服务器
关于这个,本人在调试过程中深有体会,移植好lwip后,直接ping ip地址一切正常,但是无论怎样就是解析不了域名,完整了跟了dns.c的代码,硬是没有找到哪里有问题,后来还是用ntp时发现的,ntp默认选择了阿里的一个服务器,于是灵机一动,dns当初好像没有这是默认服务器,中间也没有去指定.心累.
core/dns.c:92:#define DNS_SERVER_ADDRESS(ipaddr) (ip4_addr_set_u32(ipaddr, ipaddr_addr(“208.67.222.222”))) /*resolver1.opendns.com */
这样就ok了.
10.2设置dns_pcb控制块
初始化dns table

11.ppp_init()
点对点协议还没有去了解过,这里拿出来大家一起看看吧.

/************************************/
/*** PRIVATE FUNCTION DEFINITIONS ***/
/************************************/
 
/* Initialize the PPP subsystem. */
int ppp_init(void)
{
#if PPPOS_SUPPORT
  LWIP_MEMPOOL_INIT(PPPOS_PCB);
#endif
#if PPPOE_SUPPORT
  LWIP_MEMPOOL_INIT(PPPOE_IF);
#endif
#if PPPOL2TP_SUPPORT
  LWIP_MEMPOOL_INIT(PPPOL2TP_PCB);
#endif
#if LWIP_PPP_API && LWIP_MPU_COMPATIBLE
  LWIP_MEMPOOL_INIT(PPPAPI_MSG);
#endif
 
  LWIP_MEMPOOL_INIT(PPP_PCB);
 
  /*
   * Initialize magic number generator now so that protocols may
   * use magic numbers in initialization.
   */
  magic_init();
 
  return 0;
}          

粗略看好像就是两个动作:初始化一些内存池子,然后初始化一个随机数.有什么用?鄙人也不清楚.emm

12.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, lwip_cyclic_timer, LWIP_CONST_CAST(void *, &lwip_cyclic_timers[i]));
  }
} 

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

/** 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 */
#if LWIP_IPV6_DHCP6
  {DHCP6_TIMER_MSECS, HANDLER(dhcp6_tmr)},
#endif /* LWIP_IPV6_DHCP6 */
#endif /* LWIP_IPV6 */
};

都是xxx_tmr()函数,前面鼓励过大家在调试时大胆的把所有DEBUG宏打开,如果这样做了,你应该就会看到很多xxx_tmr的打印.
比如TCP断开连接等等,都是需要定时器参与的.就知道这一点,足够了.
好了,这一期讲的有点儿多了,不过总算是瞎扯完了.还有很多细节的东西大家自己去挖掘吧!

-------------------------------------------
这期就到这里了,LWIP想怎么玩就怎么玩,我们下期再见.

你可能感兴趣的:(#,lwip专栏)