LWIP协议栈之select

  • 遇到一个使用lwip协议栈中的select函数的问题。
  • 在使用lwip中的select时发现,当socket的负荷上升以后,select经常会等到timeout时间之后才会返回socket可用。那究竟是怎么一回事呢?我们先简单了解几个概念。
    1.lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈
    2.select函数原型:int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
int lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout)
{
  u32_t waitres = 0;
  int nready;
  fd_set lreadset, lwriteset, lexceptset;
  u32_t msectimeout;
  struct lwip_select_cb select_cb;
  err_t err;
  int i;
  SYS_ARCH_DECL_PROTECT(lev);
 
  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n",
                  maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset,
                  timeout ? (s32_t)timeout->tv_sec : (s32_t)-1,
                  timeout ? (s32_t)timeout->tv_usec : (s32_t)-1));
 
  /* Go through each socket in each list to count number of sockets which
     currently match */
  nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);       //根据每个socket发生的事件更新文件描述符
 
  /* If we don't have any current events, then suspend if we are supposed to */
  if (!nready)       
  {
    if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0)
    {
      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n"));
      /* This is OK as the local fdsets are empty and nready is zero,
         or we would have returned earlier. */
      goto return_copy_fdsets;
    }
 
    /* None ready: add our semaphore to list:
       We don't actually need any dynamic memory. Our entry on the
       list is only valid while we are in this function, so it's ok
       to use local variables. */
 
    select_cb.next = NULL;
    select_cb.prev = NULL;
    select_cb.readset = readset;
    select_cb.writeset = writeset;
    select_cb.exceptset = exceptset;
    select_cb.sem_signalled = 0;
    err = sys_sem_new(&select_cb.sem, 0);
    if (err != ERR_OK)
    {
      /* failed to create semaphore */
      set_errno(ENOMEM);
      return -1;
    }
 
    /* Protect the select_cb_list */
    SYS_ARCH_PROTECT(lev);
 
    /* Put this select_cb on top of list */
    select_cb.next = select_cb_list;
    if (select_cb_list != NULL)
    {
      select_cb_list->prev = &select_cb;
    }
    select_cb_list = &select_cb;
    /* Increasing this counter tells even_callback that the list has changed. */
    select_cb_ctr++;
 
    /* Now we can safely unprotect */
    SYS_ARCH_UNPROTECT(lev);
 
    /* Increase select_waiting for each socket we are interested in */
    for(i = 0; i < maxfdp1; i++)      //遍历每个socket
    {
      if ((readset && FD_ISSET(i, readset)) ||
          (writeset && FD_ISSET(i, writeset)) ||
          (exceptset && FD_ISSET(i, exceptset)))
      {
        struct lwip_sock *sock = tryget_socket(i);
        LWIP_ASSERT("sock != NULL", sock != NULL);
        SYS_ARCH_PROTECT(lev);
        sock->select_waiting++;
        LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
        SYS_ARCH_UNPROTECT(lev);
      }
    }
 
    /* Call lwip_selscan again: there could have been events between
       the last scan (whithout us on the list) and putting us on the list! */
    nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
    if (!nready)
    {
      /* Still none ready, just wait to be woken */
      if (timeout == 0)
      {
        /* Wait forever */
        msectimeout = 0;
      }
      else
      {
        msectimeout =  ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000));
        if (msectimeout == 0)
        {
          /* Wait 1ms at least (0 means wait forever) */
          msectimeout = 1;
        }
      }
 
      waitres = sys_arch_sem_wait(&select_cb.sem, msectimeout);
    }
    /* Increase select_waiting for each socket we are interested in */
 
    for(i = 0; i < maxfdp1; i++)
    {
      if ((readset && FD_ISSET(i, readset)) ||
          (writeset && FD_ISSET(i, writeset)) ||
          (exceptset && FD_ISSET(i, exceptset))) {
        struct lwip_sock *sock = tryget_socket(i);
        LWIP_ASSERT("sock != NULL", sock != NULL);
        SYS_ARCH_PROTECT(lev);
        sock->select_waiting--;
        LWIP_ASSERT("sock->select_waiting >= 0", sock->select_waiting >= 0);
        SYS_ARCH_UNPROTECT(lev);
      }
    }
    /* Take us off the list */
    SYS_ARCH_PROTECT(lev);
    if (select_cb.next != NULL)
    {
      select_cb.next->prev = select_cb.prev;
    }
 
    if (select_cb_list == &select_cb)
    {       
       
      LWIP_ASSERT("select_cb.prev == NULL\n", select_cb.prev == NULL);     
      select_cb_list = select_cb.next;
 
    }
    else
    {
      LWIP_ASSERT("select_cb.prev != NULL\n", select_cb.prev != NULL);
      select_cb.prev->next = select_cb.next;
    }
    /* Increasing this counter tells even_callback that the list has changed. */
    select_cb_ctr++;
    SYS_ARCH_UNPROTECT(lev);
 
    sys_sem_free(&select_cb.sem);
    if (waitres == SYS_ARCH_TIMEOUT)
    {
      /* Timeout */
      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n"));
      /* This is OK as the local fdsets are empty and nready is zero,
         or we would have returned earlier. */
      goto return_copy_fdsets;
    }
 
    /* See what's set */
    nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
  }
 
  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
return_copy_fdsets:
  set_errno(0);
  if (readset)
  {
    *readset = lreadset;
  }
  if (writeset)
  {
    *writeset = lwriteset;
  }
  if (exceptset)
  {
    *exceptset = lexceptset;
  }
  return nready;
}

可以分析得到lwip协议栈对socket状态的监控只有两次一次是刚开始进入select的时候另一次就是timeout超时的时候。这是一种简化的协议栈。所以需要我们将timeout配置为合适的时间。

内核协议栈
所以在使用select的时候,如果没有指定timeout的话,在执行do_select函数的时候,会主动poll所有监听的fd,如果在第一次poll的时候,没有任何事件发生,那么用户态进程会睡眠,在有新的事件到来的时候,一般在write函数中使用wake_up()来唤醒等待队列上所有的waiter,即执行waiter的唤醒函数(pollwake),睡眠的进程会被唤醒,然后接着执行for循环,接着主动调用poll函数,来查看是否有对应的事件产生。所以可以看出来,select函数是一直在轮询,即使对应fd上产生了事件,将进程环境后,依然要再主动调用poll函数,来查看产生了什么事件。
引用自 : https://blog.csdn.net/sinat_33822516/article/details/113736601

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值