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想怎麼玩就怎麼玩,我們下期再見.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章