一、目的
如果有小伙伴移植过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的消息循环就讲解完了,实现上还是很容易理解的。