LwIP学习笔记—find_entry

初次接续网络,花了大把时间啃源代码,好在数据组织结构不是很复杂,一路下来还算顺利.

此次写下这篇博客,仅作为本人学习路上的一篇学习笔记

 

函数find_entry的本质是做一个查找,对缓存表进行顺序的查找,可以分为3个步骤

第一步:查找整个缓存表,找出和目的IP地址匹配的表项(该表项一定处于pending或stable状态),若找到,则返回位置引索,找不到,进行下一步

第二步:说明表中没有相关表项,需要创建一个新表项,这时要查找第一个状态为empty的表项,若找到,则返回位置引索,若找不到,进行下一步

第三步:到这里,说明所有的表项都被使用了,如果调用函数的flags参数被设置为ETHARP_TRY_HARD,那么这一步必须执行,即找缓存表中最合适的表项,删除掉,给新表项使用

内核使用一次查找便可以完成上述三步工作,过程中使用了多个局部变量,分别记录存在时间最长的各状态表项

 

内核选择合适的引索顺序为:(1)empty状态表项的引索

                                   (2)存在时间最长的stable状态表项

             (3)存在时间最长且无数据缓冲的pending表项

             (4)存在时间最长且有数据缓冲的pending表项

显然处于pending状态且有数据缓冲的表项被看作是最重要的

 

/*************************************************************************************************************
 * 函数功能  :为ipaddr寻找一个匹配的表项或者建立一个新的表项
 * 参数ipaddr:在ARP缓存表中要查找的或建立的ip地址
 * 参数flags :是否硬性建立ARP表项
 ************************************************************************************************************/
static s8_t
#if LWIP_NETIF_HWADDRHINT
    find_entry(struct ip_addr *ipaddr, u8_t flags, struct netif *netif)
#else /* LWIP_NETIF_HWADDRHINT */
    find_entry(struct ip_addr *ipaddr, u8_t flags)
#endif /* LWIP_NETIF_HWADDRHINT */
{
  s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE;
  s8_t empty = ARP_TABLE_SIZE;
  u8_t i = 0, age_pending = 0, age_stable = 0;   //记录pending,stable表项的存在时间
#if ARP_QUEUEING
  s8_t old_queue = ARP_TABLE_SIZE;               //记录存在时间最长且缓冲队列不为空的pending表项
  u8_t age_queue = 0;                            //old_queue表项的存在时间
#endif

  //首先检测上次访问这个函数所保存的IP地址是否与此次一致,如果是,可以快速返回引索
  if (ipaddr)                                    //IP地址非空
    {
#if LWIP_NETIF_HWADDRHINT                        //这条宏被定义为0
    if ((netif != NULL) && (netif->addr_hint != NULL))
    {
      /* per-pcb cached entry was given */
      u8_t per_pcb_cache = *(netif->addr_hint);
      if ((per_pcb_cache < ARP_TABLE_SIZE) && arp_table[per_pcb_cache].state == ETHARP_STATE_STABLE) 
      {
        /* the per-pcb-cached entry is stable */
        if (ip_addr_cmp(ipaddr, &arp_table[per_pcb_cache].ipaddr)) 
        {
          /* per-pcb cached entry was the right one! */
          ETHARP_STATS_INC(etharp.cachehit);
          return per_pcb_cache;
        }
      }
    }
#else /* #if LWIP_NETIF_HWADDRHINT */
        
        //etharp_cached_entry保存了上次返回的引索
    if (arp_table[etharp_cached_entry].state == ETHARP_STATE_STABLE)   //检测上次建立的ARP表项是否还有效
    {
      if (ip_addr_cmp(ipaddr, &arp_table[etharp_cached_entry].ipaddr)) //检测上次的IP地址与此次是否一致
      {
         //要找的就是这个IP
         ETHARP_STATS_INC(etharp.cachehit);
         return etharp_cached_entry;                                   //返回引索
      }
    }
#endif /* #if LWIP_NETIF_HWADDRHINT */
  }

    
    
  /************************************************************
   * 以下代码要完成三个工作:                                     
   * a) 在缓存表中查找,并记录候选表项                             
   * b) 选择候选表项                                           
   * c) 创建新表项                                             
   ************************************************************
   *    单一查找,记录以下信息                                 
   * 1) 第一个空表项的位置(如果存在)                          
   * 2) 存在时间最长的stable表项 (如果存在)                   
   * 3) 存在时间最长且缓冲队列为空的pending表项 (如果存在)    
   * 4) 存在时间最长且缓冲队列不为空的pending表项 (如果存在)  
   * 5) 查找与IP匹配的表项,不管该表项是pending还是stable状态  
   *    直到以上5项工作都完成,或者遍历了整个缓存表            
   ************************************************************/

  for (i = 0; i < ARP_TABLE_SIZE; ++i) 
  {
    //这个if只会进入一次,当发现第一个空表项时进入,并记录第一个空表项的位置
    if ((empty == ARP_TABLE_SIZE) && (arp_table[i].state == ETHARP_STATE_EMPTY)) 
    {
      LWIP_DEBUGF(ETHARP_DEBUG, ("find_entry: found empty entry %"U16_F"\n", (u16_t)i));

      empty = i;                                                 //记录第一个空表项的位置
    }

    else if (arp_table[i].state == ETHARP_STATE_PENDING)         //若表项为pending状态
    {
      if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr))   //检查IP是否匹配,匹配的话就直接返回引索
      {
        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: found matching pending entry %"U16_F"\n", (u16_t)i));
 
#if LWIP_NETIF_HWADDRHINT
        NETIF_SET_HINT(netif, i);
#else /* #if LWIP_NETIF_HWADDRHINT */
        etharp_cached_entry = i;                                 //记录本次返回的位置引索
#endif /* #if LWIP_NETIF_HWADDRHINT */
        return i;                                                //匹配,返回引索
#if ARP_QUEUEING
      } 
      else if (arp_table[i].q != NULL)                           //如果IP不匹配且缓冲队列不为空
      {
        if (arp_table[i].ctime >= age_queue)                     //如果该表项存在时间比old_queue记录的更长
        {
          old_queue = i;                                         //更新old_queue
          age_queue = arp_table[i].ctime;                        //更新age_queue
        }                                                        //注:此处结合前面的循环,可找到缓冲队列不为空且存在时间最长的pending表项
#endif
      }
      else                                                       //如果IP不匹配且缓冲队列为空
      {
        if (arp_table[i].ctime >= age_pending)                   //如果该表项存在时间比old_pending记录的更长
        {
          old_pending = i;                                       //更新old_pending
          age_pending = arp_table[i].ctime;                      //更新age_pending
        }                                                        //注:此处结合前面的循环,可找出缓冲队列为空且存在时间最长的pending表项
      }        
    }
    else if (arp_table[i].state == ETHARP_STATE_STABLE)          //当前表项为stable状态
    {
      if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr))   //检查IP是否匹配,匹配的话就直接返回引索
      {
        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: found matching stable entry %"U16_F"\n", (u16_t)i));
#if LWIP_NETIF_HWADDRHINT
        NETIF_SET_HINT(netif, i);
#else /* #if LWIP_NETIF_HWADDRHINT */
        etharp_cached_entry = i;
#endif /* #if LWIP_NETIF_HWADDRHINT */
        return i;                                                //匹配,返回引索
      }                                                          //查找存在时间最长的stable entry
      else if (arp_table[i].ctime >= age_stable)                 //若IP不匹配且存在时间比age_stable记录的更长
      {
        old_stable = i;                                          //更新old_stable
        age_stable = arp_table[i].ctime;                         //更新age_stable
      }                                                          //注:此处结合前面的循环,可找出存在时间最长的stable表项
    }
  }
  
  //到这里函数未返回,则说明没有找到匹配的表项,需要创建一个新的表项
  
  //如果没有找到空表项,且不允许回收表项,那就返回错误
  if (((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_TRY_HARD) == 0))
      || ((flags & ETHARP_FIND_ONLY) != 0)) 
  {
    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: no empty entry found and not allowed to recycle\n"));
    return (s8_t)ERR_MEM;                                        //返回错误
  }
    
    
   /**********************************************************
   * 选择对系统破坏最小的表项进行回收                        
   * 1) 空表项                                               
   * 2) 存在时间最长的stable表项                             
   * 3) 存在时间最长且缓冲队列为空的pending表项              
   * 4) 存在时间最长且缓冲队列挂载了数据包的pending表项      
   *                                                         
   * 注:以下操作建立在ETHARP_TRY_HARD设置为硬性建表的基础上 
   **********************************************************/ 

  //还存在空表项,创建一个新表项
  if (empty < ARP_TABLE_SIZE) 
  {
    i = empty;
    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting empty entry %"U16_F"\n", (u16_t)i));
  }
  //没有空表项,回收存在时间最长的stable表项
  else if (old_stable < ARP_TABLE_SIZE) 
  {
    i = old_stable;                                   //记录表项位置
    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i));
#if ARP_QUEUEING
    //如果该表项等待队列中没有数据包,则不打印信息。如果队列中存在数据包,则打印一条错误提示信息
    LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL);
#endif
  } 
  //选择回收stable表项失败,回收存在时间最长且没有数据包缓冲的penging状态表项
  else if (old_pending < ARP_TABLE_SIZE) 
  {
    i = old_pending;                                             //记录表项位置
    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i));
#if ARP_QUEUEING
  } 
  //选择回收stable和无缓冲的pending表项均失败,回收存在时间最长且有数据包挂载的pending状态表项
  else if (old_queue < ARP_TABLE_SIZE) 
  {
    i = old_queue;                                               //记录表项位置
    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q)));
    free_etharp_q(arp_table[i].q);                               //首先释放等待队列中的数据包
    arp_table[i].q = NULL;                           //队列指针清空
#endif
  //没有空表项且没有可回收的表项
  } 
  else 
  {
    return (s8_t)ERR_MEM;                        //返回错误
  }

  LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);

  if (arp_table[i].state != ETHARP_STATE_EMPTY)           //如果选择的表项不是空表项,则进行以下处理
  { 
    snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr);
  }
  
  //回收表项,并将该表项状态置位空
  arp_table[i].state = ETHARP_STATE_EMPTY;

  //到这里,新表项已经创建成功
  if (ipaddr != NULL) 
  {
    ip_addr_set(&arp_table[i].ipaddr, ipaddr);                   //设置IP地址
  }
  arp_table[i].ctime = 0;                                        //ctime清零
#if LWIP_NETIF_HWADDRHINT
  NETIF_SET_HINT(netif, i);
#else /* #if LWIP_NETIF_HWADDRHINT */
  etharp_cached_entry = i;                                       //记录本次返回的位置引索
#endif /* #if LWIP_NETIF_HWADDRHINT */
  return (err_t)i;                                               //返回表项位置
}

 

posted @ 2016-12-10 11:10  应家三千金  阅读(1173)  评论(1编辑  收藏  举报