LwIP系列--线程通信消息结构

news2024/10/2 6:25:36

一、目的

如果有小伙伴移植过LwIP,那么你肯定知道在LwIP源码中tcp/ip协议栈是作为一个单独的线程运行的,那么就有这样一个问题,我们从mac外设上收到的以太网数据包是如何交给tcp/ip线程进行处理的,用户发送的数据又是如何经过协议栈处理后再发送到mac外设上的,其消息处理机制具体是怎样的?

本篇就带领大家讲讲LwIP的消息流以及消息结构。

上图就是LwIP中的消息流以及各个线程间的关系图。其中tcpip_thread线程由主线程调用tcpip_init函数进行创建,其主要负责各个协议的处理;以太网的数据经过消息邮箱送入协议栈后进行处理,协议栈处理后的消息要么发送给主线程,要么通过mac外设发送出去;用户程序通过LwIP提供的接口进行数据收发。

至于以太网数据包如何从Mac外设上收取或者发送到硬件设备上,这边有两种方法:

  • 通过收发线程转发(即上图最左边部分),收发线程再和mac外设中断或者DMA中断进行交互(RT-Thread的做法);这样做的优点是可以根据应用特点决定收发线程的优先级(例如应用发送数据比较多,那么发送的优先级就设置高一点),收发线程以代理的角色存在,避免中断和tcpip_thread线程直接打交道;这样做可能会多占用一些内存。

  • 通过中断直接收发,当以太网数据包到来时直接在中断中将消息以邮箱的形式发给tcpip_thread线程;要发送数据时,直接通过DMA或者中断进行处理;这样做的缺点是中断的快入快出可能受到一定的影响。

二、介绍

通过上面的描述我们可以知道tcpip_thread线程是一个基于邮箱的消息循环,那么如何定义一个消息体就显得至关重要。个人认为LwIP中的消息结构的定义是除了PBUF结构设计外又一神来之笔,在实际工程应用中很具有参考价值;话不多说,让我们切入正题。

消息结构

struct tcpip_msg {
  enum tcpip_msg_type type;
  union {
#if !LWIP_TCPIP_CORE_LOCKING
    struct {
      tcpip_callback_fn function;
      void* msg;
    } api_msg;
    struct {
      tcpip_api_call_fn function;
      struct tcpip_api_call_data *arg;
      sys_sem_t *sem;
    } api_call;
    struct {
      tcpip_callback_fn function;
      void *ctx;
      sys_sem_t *sem;
    } cb_wait;
#endif /* LWIP_TCPIP_CORE_LOCKING */
#if !LWIP_TCPIP_CORE_LOCKING_INPUT
    struct {
      struct pbuf *p;
      struct netif *netif;
      netif_input_fn input_fn;
    } inp;
#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
    struct {
      tcpip_callback_fn function;
      void *ctx;
    } cb;
#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
    struct {
      u32_t msecs;
      sys_timeout_handler h;
      void *arg;
    } tmo;
#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
  } msg;
};

各个字段含义:

type:消息体类型(决定了使用哪个联合体字段)

msg:联合体类型的消息体,根据不同的消息类型会有不同的消息体定义

相关宏定义

#define API_VAR_REF(name)               (*(name))
#define API_VAR_DECLARE(type, name)     type * name
#define API_VAR_ALLOC_EXT(type, pool, name, errorblock) do { \
                                          name = (type *)memp_malloc(pool); \
                                          if (name == NULL) { \
                                            errorblock; \
                                          } \
                                        } while(0)
#define API_VAR_ALLOC(type, pool, name, errorval) API_VAR_ALLOC_EXT(type, pool, name, return errorval)
#define API_VAR_ALLOC_POOL(type, pool, name, errorval) do { \
                                          name = (type *)LWIP_MEMPOOL_ALLOC(pool); \
                                          if (name == NULL) { \
                                            return errorval; \
                                          } \
                                        } while(0)
#define API_VAR_FREE(pool, name)        memp_free(pool, name)
#define API_VAR_FREE_POOL(pool, name)   LWIP_MEMPOOL_FREE(pool, name)


#define TCPIP_MSG_VAR_REF(name)     API_VAR_REF(name)
#define TCPIP_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct tcpip_msg, name)
#define TCPIP_MSG_VAR_ALLOC(name)   API_VAR_ALLOC(struct tcpip_msg, MEMP_TCPIP_MSG_API, name, ERR_MEM)
#define TCPIP_MSG_VAR_FREE(name)    API_VAR_FREE(MEMP_TCPIP_MSG_API, name)
#define API_VAR_DECLARE(type, name)     type * name

声明一个type类型的指针变量,例如

API_VAR_DECLARE(int, a);
//等同于
int *a;
#define API_VAR_REF(name)               (*(name))

解引用一个指针类型,例如

API_VAR_REF(a);
//等同于
*a;
#define API_VAR_ALLOC_EXT(type, pool, name, errorblock) do { \
                                          name = (type *)memp_malloc(pool); \
                                          if (name == NULL) { \
                                            errorblock; \
                                          } \
                                        } while(0)
#define API_VAR_ALLOC(type, pool, name, errorval) API_VAR_ALLOC_EXT(type, pool, name, return errorval)
#define API_VAR_ALLOC_POOL(type, pool, name, errorval) do { \
                                          name = (type *)LWIP_MEMPOOL_ALLOC(pool); \
                                          if (name == NULL) { \
                                            return errorval; \
                                          } \
                                        } while(0)

这几个宏定义都是从特定内存池中分配一个内存块,区别在于API_VAR_ALLOC通过memp_t类型来指定内存池,API_VAR_ALLOC_POOL通过内存池描述来分配

#define TCPIP_MSG_VAR_REF(name)     API_VAR_REF(name)
#define TCPIP_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct tcpip_msg, name)
#define TCPIP_MSG_VAR_ALLOC(name)   API_VAR_ALLOC(struct tcpip_msg, MEMP_TCPIP_MSG_API, name, ERR_MEM)
#define TCPIP_MSG_VAR_FREE(name)    API_VAR_FREE(MEMP_TCPIP_MSG_API, name)

上面几个宏就是用于操作tcpip_msg结构体变量的,举例

TCPIP_MSG_VAR_DECLARE(foo);  //声明struct tcpip_msg类型的指针变量foo
//等同于
struct tcpip_msg *foo;

TCPIP_MSG_VAR_REF(foo);   //struct tcpip_msg类型的指针变量foo解引用
//等同于
*foo;

TCPIP_MSG_VAR_ALLOC(foo);  //从MEMP_TCPIP_MSG_API内存池中分配消息结构,并赋值给foo指针
//等同于
API_VAR_ALLOC(struct tcpip_msg, MEMP_TCPIP_MSG_API, foo, ERR_MEM)

TCPIP_MSG_VAR_FREE(foo);  //将foo占用的内存释放会内存池
//等同于
API_VAR_FREE(MEMP_TCPIP_MSG_API, foo);

在介绍各个具体消息体之前我们先对消息类型进行说明

enum tcpip_msg_type {
#if !LWIP_TCPIP_CORE_LOCKING
  TCPIP_MSG_API,
  TCPIP_MSG_API_CALL,
#endif /* !LWIP_TCPIP_CORE_LOCKING */
#if !LWIP_TCPIP_CORE_LOCKING_INPUT
  TCPIP_MSG_INPKT,
#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
  TCPIP_MSG_TIMEOUT,
  TCPIP_MSG_UNTIMEOUT,
#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
  TCPIP_MSG_CALLBACK,
  TCPIP_MSG_CALLBACK_STATIC,
  TCPIP_MSG_CALLBACK_STATIC_WAIT
};

TCPIP_MSG_API

用于在tcpip_thread线程中执行指定的函数,对应的消息体结构为api_msg

/** Function prototype for functions passed to tcpip_callback() */
typedef void (*tcpip_callback_fn)(void *ctx);
struct {
    tcpip_callback_fn function;
    void* msg;
} api_msg;

各字段含义:

function:需要执行的函数,有一个void*的入参,无返回值

msg:对应函数的入参

此消息的发送函数

/**
 * Sends a message to TCPIP thread to call a function. Caller thread blocks on
 * on a provided semaphore, which ist NOT automatically signalled by TCPIP thread,
 * this has to be done by the user.
 * It is recommended to use LWIP_TCPIP_CORE_LOCKING since this is the way
 * with least runtime overhead.
 *
 * @param fn function to be called from TCPIP thread
 * @param apimsg argument to API function
 * @param sem semaphore to wait on
 * @return ERR_OK if the function was called, another err_t if not
 */
err_t
tcpip_send_msg_wait_sem(tcpip_callback_fn fn, void *apimsg, sys_sem_t *sem)
{
#if LWIP_TCPIP_CORE_LOCKING
  LWIP_UNUSED_ARG(sem);
  LOCK_TCPIP_CORE();
  fn(apimsg);
  UNLOCK_TCPIP_CORE();
  return ERR_OK;
#else /* LWIP_TCPIP_CORE_LOCKING */
  TCPIP_MSG_VAR_DECLARE(msg);  //①

  LWIP_ASSERT("semaphore not initialized", sys_sem_valid(sem));
  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));

  TCPIP_MSG_VAR_ALLOC(msg);   //②
  TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API;   //③
  TCPIP_MSG_VAR_REF(msg).msg.api_msg.function = fn;   
  TCPIP_MSG_VAR_REF(msg).msg.api_msg.msg = apimsg;
  sys_mbox_post(&tcpip_mbox, &TCPIP_MSG_VAR_REF(msg));  //④
  sys_arch_sem_wait(sem, 0);
  TCPIP_MSG_VAR_FREE(msg);
  return ERR_OK;
#endif /* LWIP_TCPIP_CORE_LOCKING */
}

①声明消息结构指针变量

②从内存池中分配消息体内存

③设置消息类型并设置消息体各个字段

④通过邮箱发送消息

//①②③的操作等同于
struct tcpip_msg *msg = memp_malloc(MEMP_TCPIP_MSG_API);
*msg.type = TCPIP_MSG_API;
*msg.msg.api_msg.function = fn;
*msg.msg.api_msg.msg = apimsg;

此消息的处理过程

/* Handle a single tcpip_msg
 * This is in its own function for access by tests only.
 */
static void
tcpip_thread_handle_msg(struct tcpip_msg *msg)
{
  switch (msg->type) {
#if !LWIP_TCPIP_CORE_LOCKING
    case TCPIP_MSG_API:   //①
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
      msg->msg.api_msg.function(msg->msg.api_msg.msg);  //②
      break;
    case TCPIP_MSG_API_CALL:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API CALL message %p\n", (void *)msg));
      msg->msg.api_call.arg->err = msg->msg.api_call.function(msg->msg.api_call.arg);
      sys_sem_signal(msg->msg.api_call.sem);
      break;
#endif /* !LWIP_TCPIP_CORE_LOCKING */

#if !LWIP_TCPIP_CORE_LOCKING_INPUT
    case TCPIP_MSG_INPKT:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
      if (msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif) != ERR_OK) {
        pbuf_free(msg->msg.inp.p);
      }
      memp_free(MEMP_TCPIP_MSG_INPKT, msg);
      break;
#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */

#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
    case TCPIP_MSG_TIMEOUT:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg));
      sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
      memp_free(MEMP_TCPIP_MSG_API, msg);
      break;
    case TCPIP_MSG_UNTIMEOUT:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg));
      sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
      memp_free(MEMP_TCPIP_MSG_API, msg);
      break;
#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */

    case TCPIP_MSG_CALLBACK:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
      msg->msg.cb.function(msg->msg.cb.ctx);
      memp_free(MEMP_TCPIP_MSG_API, msg);
      break;

    case TCPIP_MSG_CALLBACK_STATIC:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg));
      msg->msg.cb.function(msg->msg.cb.ctx);
      break;

    default:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type));
      LWIP_ASSERT("tcpip_thread: invalid message", 0);
      break;
  }
}

上面的片段①②就是TCPIP_MSG_API的处理,也就是在tcpip_thread线程中执行消息体上指定的函数。


TCPIP_MSG_API_CALL

用于在tcpip_thread线程中执行指定的函数,区别于api_msg,调用方需要等待该函数执行完毕后才返回,对应的消息体结构为api_msg_call

struct tcpip_api_call_data
{
#if !LWIP_TCPIP_CORE_LOCKING
  err_t err;
#if !LWIP_NETCONN_SEM_PER_THREAD
  sys_sem_t sem;
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
#else /* !LWIP_TCPIP_CORE_LOCKING */
  u8_t dummy; /* avoid empty struct :-( */
#endif /* !LWIP_TCPIP_CORE_LOCKING */
};   
typedef err_t (*tcpip_api_call_fn)(struct tcpip_api_call_data* call);

struct {
    tcpip_api_call_fn function;
    struct tcpip_api_call_data *arg;
    sys_sem_t *sem;
} api_call;

各字段含义:

function:需要执行的函数,该函数有一个struct tcpip_api_call_data类型的指针参数,返回值类型为err_t

arg:对应函数的入参

sem:用于通知调用方退出阻塞的信号量

此消息的发送函数

/**
 * Synchronously calls function in TCPIP thread and waits for its completion.
 * It is recommended to use LWIP_TCPIP_CORE_LOCKING (preferred) or
 * LWIP_NETCONN_SEM_PER_THREAD.
 * If not, a semaphore is created and destroyed on every call which is usually
 * an expensive/slow operation.
 * @param fn Function to call
 * @param call Call parameters
 * @return Return value from tcpip_api_call_fn
 */
err_t
tcpip_api_call(tcpip_api_call_fn fn, struct tcpip_api_call_data *call)
{
#if LWIP_TCPIP_CORE_LOCKING
  err_t err;
  LOCK_TCPIP_CORE();
  err = fn(call);
  UNLOCK_TCPIP_CORE();
  return err;
#else /* LWIP_TCPIP_CORE_LOCKING */
  TCPIP_MSG_VAR_DECLARE(msg);  //①

#if !LWIP_NETCONN_SEM_PER_THREAD
  err_t err = sys_sem_new(&call->sem, 0);
  if (err != ERR_OK) {
    return err;
  }
#endif /* LWIP_NETCONN_SEM_PER_THREAD */

  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));

  TCPIP_MSG_VAR_ALLOC(msg); //②
  TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API_CALL;
  TCPIP_MSG_VAR_REF(msg).msg.api_call.arg = call;
  TCPIP_MSG_VAR_REF(msg).msg.api_call.function = fn;
#if LWIP_NETCONN_SEM_PER_THREAD
  TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = LWIP_NETCONN_THREAD_SEM_GET(); //③
#else /* LWIP_NETCONN_SEM_PER_THREAD */
  TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = &call->sem;
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
  sys_mbox_post(&tcpip_mbox, &TCPIP_MSG_VAR_REF(msg));  //④
  sys_arch_sem_wait(TCPIP_MSG_VAR_REF(msg).msg.api_call.sem, 0); //⑤
  TCPIP_MSG_VAR_FREE(msg);  //⑥

#if !LWIP_NETCONN_SEM_PER_THREAD
  sys_sem_free(&call->sem);
#endif /* LWIP_NETCONN_SEM_PER_THREAD */

  return call->err;
#endif /* LWIP_TCPIP_CORE_LOCKING */
}

①声明消息结构指针变量

②从内存池中分配消息体内存

③设置消息类型并设置消息体各个字段,注意此处设置了一个信号量用于同步

④通过邮箱发送消息

⑤阻塞等待信号量上直到这个函数执行完毕,

此消息的处理过程

/* Handle a single tcpip_msg
 * This is in its own function for access by tests only.
 */
static void
tcpip_thread_handle_msg(struct tcpip_msg *msg)
{
  switch (msg->type) {
#if !LWIP_TCPIP_CORE_LOCKING
    case TCPIP_MSG_API:   
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
      msg->msg.api_msg.function(msg->msg.api_msg.msg); 
      break;
    case TCPIP_MSG_API_CALL: //①
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API CALL message %p\n", (void *)msg));
      msg->msg.api_call.arg->err = msg->msg.api_call.function(msg->msg.api_call.arg); //②
      sys_sem_signal(msg->msg.api_call.sem); //③
      break;
#endif /* !LWIP_TCPIP_CORE_LOCKING */

#if !LWIP_TCPIP_CORE_LOCKING_INPUT
    case TCPIP_MSG_INPKT:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
      if (msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif) != ERR_OK) {
        pbuf_free(msg->msg.inp.p);
      }
      memp_free(MEMP_TCPIP_MSG_INPKT, msg);
      break;
#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */

#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
    case TCPIP_MSG_TIMEOUT:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg));
      sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
      memp_free(MEMP_TCPIP_MSG_API, msg);
      break;
    case TCPIP_MSG_UNTIMEOUT:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg));
      sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
      memp_free(MEMP_TCPIP_MSG_API, msg);
      break;
#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */

    case TCPIP_MSG_CALLBACK:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
      msg->msg.cb.function(msg->msg.cb.ctx);
      memp_free(MEMP_TCPIP_MSG_API, msg);
      break;

    case TCPIP_MSG_CALLBACK_STATIC:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg));
      msg->msg.cb.function(msg->msg.cb.ctx);
      break;

    default:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type));
      LWIP_ASSERT("tcpip_thread: invalid message", 0);
      break;
  }
}

上面的片段①②③就是TCPIP_MSG_API_CALL的处理,也就是在tcpip_thread线程中执行消息体上指定的函数,注意③操作发出信号量


TCPIP_MSG_INPKT

用于在tcpip_thread线程中通过指定的函数对指定的网络接口上的数据包进行处理,数据包由pbuf结构体指针p指定,执行函数由input_fn指定

/** Function prototype for netif->input functions. This function is saved as 'input'
 * callback function in the netif struct. Call it when a packet has been received.
 *
 * @param p The received packet, copied into a pbuf
 * @param inp The netif which received the packet
 * @return ERR_OK if the packet was handled
 *         != ERR_OK is the packet was NOT handled, in this case, the caller has
 *                   to free the pbuf
 */
typedef err_t (*netif_input_fn)(struct pbuf *p, struct netif *inp);

struct {
    struct pbuf *p;
    struct netif *netif;
    netif_input_fn input_fn;
} inp;

各字段含义:

p:pbuf结构的数据包

netif:从此网卡上收取的数据包

input_fun:数据包处理函数

此消息的发送过程

/**
 * Pass a received packet to tcpip_thread for input processing
 *
 * @param p the received packet
 * @param inp the network interface on which the packet was received
 * @param input_fn input function to call
 */
err_t
tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
{
#if LWIP_TCPIP_CORE_LOCKING_INPUT
  err_t ret;
  LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_inpkt: PACKET %p/%p\n", (void *)p, (void *)inp));
  LOCK_TCPIP_CORE();
  ret = input_fn(p, inp);
  UNLOCK_TCPIP_CORE();
  return ret;
#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */
  struct tcpip_msg *msg;

  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));

  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT);  //①
  if (msg == NULL) {
    return ERR_MEM;
  }

  msg->type = TCPIP_MSG_INPKT;  //②
  msg->msg.inp.p = p;
  msg->msg.inp.netif = inp;
  msg->msg.inp.input_fn = input_fn;
  if (sys_mbox_trypost(&tcpip_mbox, msg) != ERR_OK) {  //③
    memp_free(MEMP_TCPIP_MSG_INPKT, msg);
    return ERR_MEM;
  }
  return ERR_OK;
#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
}

上面的片段①②③就是TCPIP_MSG_API_CALL的发送过程,注意此消息是从MEMP_TCPIP_MSG_INPKT内存池中分配的。

此消息的处理过程

/* Handle a single tcpip_msg
 * This is in its own function for access by tests only.
 */
static void
tcpip_thread_handle_msg(struct tcpip_msg *msg)
{
  switch (msg->type) {
#if !LWIP_TCPIP_CORE_LOCKING
    case TCPIP_MSG_API:   
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
      msg->msg.api_msg.function(msg->msg.api_msg.msg); 
      break;
    case TCPIP_MSG_API_CALL:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API CALL message %p\n", (void *)msg));
      msg->msg.api_call.arg->err = msg->msg.api_call.function(msg->msg.api_call.arg); 
      sys_sem_signal(msg->msg.api_call.sem); 
      break;
#endif /* !LWIP_TCPIP_CORE_LOCKING */

#if !LWIP_TCPIP_CORE_LOCKING_INPUT
    case TCPIP_MSG_INPKT: //①
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
      if (msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif) != ERR_OK) { //②
        pbuf_free(msg->msg.inp.p); //③
      }
      memp_free(MEMP_TCPIP_MSG_INPKT, msg); //④
      break;
#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */

#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
    case TCPIP_MSG_TIMEOUT:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg));
      sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
      memp_free(MEMP_TCPIP_MSG_API, msg);
      break;
    case TCPIP_MSG_UNTIMEOUT:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg));
      sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
      memp_free(MEMP_TCPIP_MSG_API, msg);
      break;
#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */

    case TCPIP_MSG_CALLBACK:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
      msg->msg.cb.function(msg->msg.cb.ctx);
      memp_free(MEMP_TCPIP_MSG_API, msg);
      break;

    case TCPIP_MSG_CALLBACK_STATIC:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg));
      msg->msg.cb.function(msg->msg.cb.ctx);
      break;

    default:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type));
      LWIP_ASSERT("tcpip_thread: invalid message", 0);
      break;
  }
}

上面的片段①②③④就是TCPIP_MSG_INPKT的处理,也就是在tcpip_thread线程中执行消息体上指定的数据包处理函数并释放数据包和消息体


TCPIP_MSG_TIMEOUT和TCPIP_MSG_UNTIMEOUT

用于在tcpip_thread线程中进行超时处理

struct {
    u32_t msecs;
    sys_timeout_handler h;
    void *arg;
} tmo;
/**
 * call sys_timeout in tcpip_thread
 *
 * @param msecs time in milliseconds for timeout
 * @param h function to be called on timeout
 * @param arg argument to pass to timeout function h
 * @return ERR_MEM on memory error, ERR_OK otherwise
 */
err_t
tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg)
{
  struct tcpip_msg *msg;

  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));

  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
  if (msg == NULL) {
    return ERR_MEM;
  }

  msg->type = TCPIP_MSG_TIMEOUT;
  msg->msg.tmo.msecs = msecs;
  msg->msg.tmo.h = h;
  msg->msg.tmo.arg = arg;
  sys_mbox_post(&tcpip_mbox, msg);
  return ERR_OK;
}

/**
 * call sys_untimeout in tcpip_thread
 *
 * @param h function to be called on timeout
 * @param arg argument to pass to timeout function h
 * @return ERR_MEM on memory error, ERR_OK otherwise
 */
err_t
tcpip_untimeout(sys_timeout_handler h, void *arg)
{
  struct tcpip_msg *msg;

  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));

  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
  if (msg == NULL) {
    return ERR_MEM;
  }

  msg->type = TCPIP_MSG_UNTIMEOUT;
  msg->msg.tmo.h = h;
  msg->msg.tmo.arg = arg;
  sys_mbox_post(&tcpip_mbox, msg);
  return ERR_OK;
}
case TCPIP_MSG_TIMEOUT:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg));
      sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
      memp_free(MEMP_TCPIP_MSG_API, msg);
      break;
    case TCPIP_MSG_UNTIMEOUT:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg));
      sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
      memp_free(MEMP_TCPIP_MSG_API, msg);
      break;

TCPIP_MSG_CALLBACK和TCPIP_MSG_CALLBACK_STATIC

用于在tcpip_thread线程中执行函数

    struct {
      tcpip_callback_fn function;
      void *ctx;
    } cb;
/**
 * @ingroup lwip_os
 * Call a specific function in the thread context of
 * tcpip_thread for easy access synchronization.
 * A function called in that way may access lwIP core code
 * without fearing concurrent access.
 * Blocks until the request is posted.
 * Must not be called from interrupt context!
 *
 * @param function the function to call
 * @param ctx parameter passed to f
 * @return ERR_OK if the function was called, another err_t if not
 *
 * @see tcpip_try_callback
 */
err_t
tcpip_callback(tcpip_callback_fn function, void *ctx)
{
  struct tcpip_msg *msg;

  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));

  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
  if (msg == NULL) {
    return ERR_MEM;
  }

  msg->type = TCPIP_MSG_CALLBACK;
  msg->msg.cb.function = function;
  msg->msg.cb.ctx = ctx;

  sys_mbox_post(&tcpip_mbox, msg);
  return ERR_OK;
}
/**
 * @ingroup lwip_os
 * Allocate a structure for a static callback message and initialize it.
 * The message has a special type such that lwIP never frees it.
 * This is intended to be used to send "static" messages from interrupt context,
 * e.g. the message is allocated once and posted several times from an IRQ
 * using tcpip_callbackmsg_trycallback().
 * Example usage: Trigger execution of an ethernet IRQ DPC routine in lwIP thread context.
 * 
 * @param function the function to call
 * @param ctx parameter passed to function
 * @return a struct pointer to pass to tcpip_callbackmsg_trycallback().
 *
 * @see tcpip_callbackmsg_trycallback()
 * @see tcpip_callbackmsg_delete()
 */
struct tcpip_callback_msg *
tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx)
{
  struct tcpip_msg *msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
  if (msg == NULL) {
    return NULL;
  }
  msg->type = TCPIP_MSG_CALLBACK_STATIC;
  msg->msg.cb.function = function;
  msg->msg.cb.ctx = ctx;
  return (struct tcpip_callback_msg *)msg;
}
    case TCPIP_MSG_CALLBACK:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
      msg->msg.cb.function(msg->msg.cb.ctx);
      memp_free(MEMP_TCPIP_MSG_API, msg);
      break;

    case TCPIP_MSG_CALLBACK_STATIC:
      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg));
      msg->msg.cb.function(msg->msg.cb.ctx);
      break;

至此,整个LwIP的消息循环就讲解完了,实现上还是很容易理解的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/337997.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

不学Python迟早会被淘汰?Python真有这么好的前景?

最近几年Python编程语言在国内引起不小的轰动,有超越Java之势,本来在美国这个编程语言就是最火的,应用的非常非常的广泛,而Python的整体语言难度来讲又比Java简单的很多。尤其是在运维的应用中非常的广泛,所以之前出了…

Ubuntu20.04无线网卡驱动安装

文章目录一.未安装无线网卡驱动的Ubuntu20.04联网方式二.Ubuntu20.04无线网卡驱动安装UbuntuU盘启动盘安装好Ubuntu 20.04之后,发现没有无线网络,不过有线可以用。一.未安装无线网卡驱动的Ubuntu20.04联网方式 比较简单的就是直接拉一条网线进行连接&am…

【C语言】宏定义 结构体 枚举变量的用法

目录 一、数据类型 二、C语言宏定义 三、C语言typedef重命名 四、 #define与typedef的区别 五、结构体 六、枚举变量 补充学习一点STM32的必备基础知识 一、数据类型 二、C语言宏定义 关键字:#define 用途:用一个字符串代替一个数字,…

214 情人节来袭,电视剧 《点燃我温暖你》李峋同款 Python爱心表白代码,赶紧拿去用吧

大家好,我是徐公,六年大厂程序员经验,今天为大家带来的是动态心形代码,电视剧 《点燃我温暖你》同款的,大家赶紧看看,拿去向你心仪的对象表白吧,下面说一下灵感来源。 灵感来源 今天&#xff…

2023-02-10 - 6 聚合

当用户使用搜索引擎完成搜索后,在展示结果中需要进行进一步的筛选,而筛选的维度需要根据当前的搜索结果进行汇总,这就用到了聚合技术。聚合的需求在很多应用程序中都有所体现,例如在京东App中搜索“咸鸭蛋”,然后单击搜…

简述操作系统的系统中断

系统中断 系统中断是指 CPU 对系统发生的某个事件做出的一种反应:CPU 暂停正在执行的程序,保留现场后转去执行相应的处理程序,处理完该事件后再返回断点继续执行被打断的程序。 中断可以避免 CPU 轮询等待某条件成立,减小系统开…

2023-02-10 - 5 文本搜索

与其他需要精确匹配的数据不同,文本数据在前期的索引构建和搜索环节都需要进行额外的处理,并且在匹配环节还要进行相关性分数计算。本章将详细介绍文本搜索的相关知识。 本章首先从总体上介绍文本的索引建立过程和搜索过程,然后介绍分析器的…

离子阱量子计算机

1.新闻 2020年6月,科技制造企业霍尼韦尔(Honeywell)发布第一台离子阱量子计算机H0,它拥有64量子体积,它是IBM和谷歌同时期量子计算机的两倍。公司表示之所以能取得这一成就缘于他们2015年在一篇科学论文中展示的量子电…

Docker 容器与容器云读书笔记(一)

最近都没时间看书,闲暇之余看看书,写写笔记,记录一下这难得的时光。 docker容器的出现 2013年初, 一个名字从云计算领域横空出世,并在整个IT行业激起千层浪,这就是Docker。Docker选择容器作为核心和基础&…

与六年测试工程师促膝长谈,他分享的这些让我对软件测试工作有了全新的认知~

不知不觉已经从事软件测试六年了,2016年毕业到进入外包公司外包给微软做软件测试, 到现在加入著名的外企。六年的时间过得真快。长期的测试工作也让我对软件测试有了比较深入的认识。但是我至今还是一个底层的测试人员,我的看法都比较狭隘&am…

知识点滴 - 电源适配器接头的极性

使用各种电子设备,肯定要使用到电源适配器(adapter)。在电源适配器的标签上面一般会有几项是需要注意的。第一,是adapter的“INPUT/输入”,在中国通用的一般是AC100-240V~50-60Hz,这说明这个adapter可以在100V-240V的交流电压下面…

自动驾驶过冬,需要点燃“降本增效”的炉火

进入2023年,人们还在等待这个冬天的第一场雪,同时也在感叹暖冬已经成为了新的常态。但对自动驾驶产业而言,这个冬天似乎并没有那么暖。回顾2022年,全球自动驾驶行业裁员、倒闭、部门裁撤的消息不绝于耳。资本市场也逐渐放弃了自20…

流浪气球?ChatGPT这样回答,我惊了

近日,流浪地球电影反响热烈,“流浪气球”事件讨论热火,连人工智能ChatGPT都发表了 “自己”的看法,到底是怎么一回事呢?起因是我国一只民用气球,因技术和天气原因不小心飘到了米国上空,对方当时…

vue组合式API及生命周期钩子函数

一、组合式API 什么是组合式API? vue3中支持vue2的选项式、支持新的编程模式–函数式编程(没有this指针)做了一个兼容,可以在一个组件中使用函数式编程和OOP编程(选项式) setup()函数 可以使用setup属性…

二叉树的遍历 (2023-02-11)

二叉树的遍历 二叉树的遍历分为:先序遍历、中序遍历、后序遍历和层次遍历。 1.先序遍历(根左右) (1)访问根节点 (2)左子树按根左右遍历 (3)右子树按根左右遍历 2.中序…

【Flume】高级组件之Channel Selectors及项目实践

文章目录1. 组件简介2. 项目实践2.1 Replicating Channel Selector实践2.1.1 需求2.1.2 配置2.1.3 运行2.2 Multiplexing Channel Selector实践2.2.1 需求2.2.2 配置2.2.3 运行1. 组件简介 通俗来讲,Channel Selectors组件控制Source采集到的数据分别流向哪些Channe…

Idea超好用的管理工具ToolBox(附带idea工具)

文章目录为什么要用ToolBox总结idea管理安装、更新、卸载寻找ide配置、根路径idea使用准备工作配置为什么要用ToolBox 快速轻松地更新,轻松管理您的 JetBrains 工具 安装自动更新同时更新插件和 IDE回滚和降级通过下载补丁或一组补丁而不是整个包,节省维护 IDE 的…

snakeyaml数字字符串显示单引号的问题

如题所示&#xff0c;一般yaml结构中&#xff0c;字符串直接显示没有单引号的字符串&#xff0c;如果字符串由数字组成&#xff0c;为了区别真正的数字&#xff0c;这个字符串会使用单引号包围起来。 数据结构如下&#xff1a; Map<String,Object> map new LinkedHashM…

COCO物体检测评测方法简介

本文从ap计算到map计算&#xff0c;最后到coco[0.5:0.95:0.05] map的计算&#xff0c;一步一步拆解物体检测指标map的计算方式。 一、ap计算方法 一个数据集有多个类别&#xff0c;对于该数据库有5个gt&#xff0c;算法检测出来10个bbox&#xff0c;对于人这个类别来说检测有…

类和对象实操之【日期类】

✨个人主页&#xff1a; Yohifo &#x1f389;所属专栏&#xff1a; C修行之路 &#x1f38a;每篇一句&#xff1a; 图片来源 The pessimist complains about the wind; the optimist expects it to change; the realist adjusts the sails. 悲观主义者抱怨风;乐观主义者期望它…