目录
1、初始化过程
协议栈初始化函数 lwip_init() 在 init.c 文件中。(只有在NO_SYS模式下才能直接调用此函数进行初始化)
其初始化的核心组件如下所示:
cpp
void
lwip_init(void)
{
/* Modules initialization */
stats_init();
#if !NO_SYS
sys_init();
#endif /* !NO_SYS */
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 */
}
如果协议栈移植了操作系统,则必须调用 tcpip_init 对系统进行初始化操作:
cpp
void
tcpip_init(tcpip_init_done_fn initfunc, void *arg)
{
lwip_init();
tcpip_init_done = initfunc; /* 初始化后的钩子函数 */
tcpip_init_done_arg = arg; /* 初始化后的钩子函数的参数 */
/* 创建内核邮箱 */
if (sys_mbox_new(&tcpip_mbox, TCPIP_MBOX_SIZE) != ERR_OK) {
LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
}
#if LWIP_TCPIP_CORE_LOCKING
/* 创建内核锁 */
if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) {
LWIP_ASSERT("failed to create lock_tcpip_core", 0);
}
#endif /* LWIP_TCPIP_CORE_LOCKING */
/* 创建内核线程 */
sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
}
其中除了调用 lwip_init 对内核进行初始化外,还进行了如下操作:
- 配置初始化后的钩子函数,在新内核线程
tcpip_thread中跑;- 创建一个
tcpip_mbox邮箱,成员个数为TCPIP_MBOX_SIZE。主要用于接收从底层或者上层传递过来的消息;- 创建一个
lock_tcpip_core内核锁;- 创建一个
tcpip_thread线程。这个线程就是LwIP在操作系统中作为一个独立的线程运行,所有处理的数据都是这个线程处理;
2、内核超时模块
内核超时框架的源码主要在 timeouts.c和 timeouts.h;
2.1、内核超时链表数据结构
内核超时链表:
cpp
/** The one and only timeout list */
static struct sys_timeo *next_timeout;
它的数据结构如下:
cpp
struct sys_timeo {
struct sys_timeo *next; /* 下一个节点 */
u32_t time; /* 被唤醒的时间 */
sys_timeout_handler h; /* 回调 */
void *arg; /* 回调的参数 */
#if LWIP_DEBUG_TIMERNAMES
const char* handler_name; /* 当前超时事件的描述。调试使用 */
#endif /* LWIP_DEBUG_TIMERNAMES */
};
循环定时功能也是通过内核超时模块实现的,循环定时的数据结构如下:
cpp
/** This struct contains information about a stack-internal timer function
that has to be called at a defined interval */
struct lwip_cyclic_timer {
u32_t interval_ms; /* 循环定时周期 */
lwip_cyclic_timer_handler handler; /* 每周期到期时触发的动作 */
#if LWIP_DEBUG_TIMERNAMES
const char* handler_name;
#endif
};
2.2、内核超时初始化
注册超时事件:
注册超时事件使用 sys_timeout(),内部会调用 sys_timeout_abs()把超时事件插入到超时链表 next_timeout 中:
cpp
void sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg)
{
u32_t next_timeout_time;
next_timeout_time = (u32_t)(sys_now() + msecs);
sys_timeout_abs(next_timeout_time, handler, arg);
}
计算得出当前对象下一周期的超时时间,并调用 sys_timeout_abs 函数进行链表插入处理;
超时事件插入超时链表:
超时事件插入超时链表是调用 sys_timeout_abs 函数实现的。具体如下:
cpp
static void
sys_timeout_abs(u32_t abs_time, sys_timeout_handler handler, void *arg)
{
struct sys_timeo *timeout, *t;
timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT);
if (timeout == NULL) {
return;
}
timeout->next = NULL;
timeout->h = handler;
timeout->arg = arg;
timeout->time = abs_time;
if (next_timeout == NULL) {
next_timeout = timeout;
return;
}
if (TIME_LESS_THAN(timeout->time, next_timeout->time)) {
timeout->next = next_timeout;
next_timeout = timeout;
} else {
for (t = next_timeout; t != NULL; t = t->next) {
if ((t->next == NULL) || TIME_LESS_THAN(timeout->time, t->next->time)) {
timeout->next = t->next;
t->next = timeout;
break;
}
}
}
}
具体流程: 1、从 TIMEOUT 内存池中申请一个超时节点空间,并按传入参数初始化此节点; 2、按顺序插入此节点到 next_timeout 链表中;
注销超时事件:
cpp
void
sys_untimeout(sys_timeout_handler handler, void *arg)
{
struct sys_timeo *prev_t, *t;
/* 确保在tcpip线程安全锁内 */
LWIP_ASSERT_CORE_LOCKED();
if (next_timeout == NULL) {
return;
}
for (t = next_timeout, prev_t = NULL; t != NULL; prev_t = t, t = t->next) {
if ((t->h == handler) && (t->arg == arg)) {
/* 在链表中找到该事件 */
/* 从超时链表中提除 */
if (prev_t == NULL) {
next_timeout = t->next;
} else {
prev_t->next = t->next;
}
/* 释放内存资源 */
memp_free(MEMP_SYS_TIMEOUT, t);
return;
}
}
return;
}
将超时节点移出 next_timeout 超时链表,并从 TIMEOUT 内存池中释放此节点的内存资源;
初始化内核超时模块:
cpp
/** Initialize this module */
void sys_timeouts_init(void)
{
size_t i;
/* tcp_tmr() 不用在初始化时就插入延时链表,因为还没用到 */
for (i = (LWIP_TCP ? 1 : 0); i < LWIP_ARRAYSIZE(lwip_cyclic_timers); i++) {
/* 把lwip_cyclic_timers数组保存的回调插入到超时链表中 */
sys_timeout(lwip_cyclic_timers[i].interval_ms, lwip_cyclic_timer, LWIP_CONST_CAST(void *, &lwip_cyclic_timers[i]));
}
}
将 lwip_cyclic_timers 数组里的每个类型元素,都插入到 timeout 链表中。其中插入顺序是按照 "当前时间 + 周期 = 到期时间" 来作为插入序号进行插入排列的。
其中 lwip_cyclic_timers 数组内容如下:
cpp
/* 这个数组包含所有堆栈内部的循环计时器 */
const struct lwip_cyclic_timer lwip_cyclic_timers[] = {
#if LWIP_TCP
/* TCP计时器是一种特殊情况:它不必总是运行,初始化内核超时机制时就不会将其插入超时链表。可使用tcp_timer_needed()触发从TCP启动。*/
{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_ACD
{ACD_TMR_INTERVAL, HANDLER(acd_tmr)},
#endif /* LWIP_ACD */
#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 */
};
/* lwip_num_cyclic_timers表示lwip_cyclic_timers数组有多少个成员 */
const int lwip_num_cyclic_timers = LWIP_ARRAYSIZE(lwip_cyclic_timers);
2.3、超时检查处理
裸机中:
直接定时调用 sys_check_timeouts() 函数来实现超时检查处理。
cpp
void
sys_check_timeouts(void)
{
u32_t now;
/* 确保在tcpip线程安全锁内 */
LWIP_ASSERT_CORE_LOCKED();
/* 获取当前时间 */
now = sys_now();
do { /* 把所有已经超时的事件都处理一遍 */
struct sys_timeo *tmptimeout;
sys_timeout_handler handler;
void *arg;
/* 检查释放已收到的无序报文的资源,提高资源空闲空间 */
PBUF_CHECK_FREE_OOSEQ();
tmptimeout = next_timeout;
if (tmptimeout == NULL) { /* 没有事件就直接返回 */
return;
}
if (TIME_LESS_THAN(now, tmptimeout->time)) { /* 没有到期事件也不要处理 */
return;
}
/* 处理已超时事件 */
next_timeout = tmptimeout->next; /* 先从超时链表中移除 */
handler = tmptimeout->h; /* 获取回调函数 */
arg = tmptimeout->arg; /* 获取回调函数参数 */
current_timeout_due_time = tmptimeout->time; /* 获取当前时间 */
#if LWIP_DEBUG_TIMERNAMES
if (handler != NULL) {
LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s t=%"U32_F" arg=%p\n",
tmptimeout->handler_name, sys_now() - tmptimeout->time, arg));
}
#endif /* LWIP_DEBUG_TIMERNAMES */
/* 先释放内存资源 */
memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
if (handler != NULL) {
handler(arg); /* 执行回调 */
}
LWIP_TCPIP_THREAD_ALIVE(); /* 该宏函数默认为空。其作用是用于类似看门狗的功能 */
} while (1);
}
该函数中直到处理完所有超时的节点回调,函数才会退出;
系统中:
超时检查处理在 tcpip_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg);函数被调用。这个函数会在 tcpip_thread 周期性调用,主要内容是等待 tcpip_mbox消息,是可阻塞的,如果在等待 tcpip_mbox的过程中发生超时事件,则会同时执行超时事件处理。这个函数后面会介绍到。
2.4、周期定时实现
周期定时器机制数据结构:
cpp
/* lwip周期定时机制数据结构 */
struct lwip_cyclic_timer {
u32_t interval_ms; /* 周期值 */
lwip_cyclic_timer_handler handler; /* 回调函数 */
#if LWIP_DEBUG_TIMERNAMES
const char* handler_name; /* 周期定时事件文本描述 */
#endif /* LWIP_DEBUG_TIMERNAMES */
};
周期定时器机制基函数:
cpp
void lwip_cyclic_timer(void *arg)
{
u32_t now;
u32_t next_timeout_time;
/* 超时事件的回调参数就是周期定时事件 */
const struct lwip_cyclic_timer *cyclic = (const struct lwip_cyclic_timer *)arg;
#if LWIP_DEBUG_TIMERNAMES
LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: %s()\n", cyclic->handler_name));
#endif
cyclic->handler(); /* 执行周期定时回调函数 */
now = sys_now(); /* 获取当前时间 */
/* 计算下一个唤醒当前周期定时事件的时间,应该从上一个理应唤醒时间开始计 */
next_timeout_time = (u32_t)(current_timeout_due_time + cyclic->interval_ms);
if (TIME_LESS_THAN(next_timeout_time, now)) {
/* 如果当前定时事件的下一个唤醒时间也过期了,那就重新计时,以当前时间为基准 */
#if LWIP_DEBUG_TIMERNAMES
sys_timeout_abs((u32_t)(now + cyclic->interval_ms), lwip_cyclic_timer, arg, cyclic->handler_name);
#else
/* 重新插入到超时链表,实现周期定时回调 */
sys_timeout_abs((u32_t)(now + cyclic->interval_ms), lwip_cyclic_timer, arg);
#endif
} else { /* 下一个唤醒时间还没到期 */
#if LWIP_DEBUG_TIMERNAMES
sys_timeout_abs(next_timeout_time, lwip_cyclic_timer, arg, cyclic->handler_name);
#else
/* 重新插入到超时链表,实现周期定时回调 */
sys_timeout_abs(next_timeout_time, lwip_cyclic_timer, arg);
#endif
}
}
1、一旦调用 lwip_cyclic_timer函数, 就证明存在周期性定时事件超时,所以直接执行回调函数;
2、计算下一唤醒当前周期定时器事件时间,从上一理应唤醒时间开始计时;
若当前定时事件的下一个唤醒事件也同时过期了,就重新计时,以当前时间为基准:
current_timeout_due_time 是定时器本应到期的时间。它在 sys_timeout_check 中调用 lwip_cyclic_timer 前调用,如果定时器查询滞时严重,当前定时事件的下个唤醒时间也过期了,就重新将其插入超时链表,并以当前时间为基准,防止超时事件的处理一直处于滞后状态;若下个唤醒时间没有到期,则证明定时器检查没有滞后,此时就直接将事件插入到超时链表即可;
3、lwip中的消息
lwip消息就是其它线程把业务外包到lwip内核主线程 tcpip_thread()去执行。具体分为:
- 数据包消息;
- API消息;
lwip中的消息数据结构:
由于lwip中有多种消息类型,所以数据结构使用联合体。
cpp
struct tcpip_msg {
enum tcpip_msg_type type; /* msg类型 */
union {
#if !LWIP_TCPIP_CORE_LOCKING
struct {
tcpip_callback_fn function; /* 需要内核执行的API */
void* msg; /* API的参数指针,内含多个参数 */
} api_msg; /* TCPIP_MSG_API。API消息 */
struct {
tcpip_api_call_fn function; /* 需要内核执行的API */
struct tcpip_api_call_data *arg;
sys_sem_t *sem;
} api_call; /* TCPIP_MSG_API_CALL。API回传消息 */
#endif /* LWIP_TCPIP_CORE_LOCKING */
#if !LWIP_TCPIP_CORE_LOCKING_INPUT
struct {
struct pbuf *p; /* 收到的数据包 */
struct netif *netif; /* 来自哪个网卡的 */
netif_input_fn input_fn; /* 需要传入哪个内核函数处理 */
} inp; /* TCPIP_MSG_INPKT。网络数据包消息 */
#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
struct {
tcpip_callback_fn function;
void *ctx;
} cb; /* TCPIP_MSG_CALLBACK、TCPIP_MSG_CALLBACK_STATIC。回调消息 */
#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
struct {
u32_t msecs;
sys_timeout_handler h;
void *arg;
} tmo; /* TCPIP_MSG_TIMEOUT、TCPIP_MSG_UNTIMEOUT。注册注销超时消息 */
#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
} msg;
};
其中消息类型有:
cpp
enum tcpip_msg_type {
#if !LWIP_TCPIP_CORE_LOCKING
TCPIP_MSG_API, /* API消息类型。如用户调用应用层的接口时,需要tcpip_thread执行内核函数就用这个消息类型。需要往南供给协议栈。 */
TCPIP_MSG_API_CALL, /* API回传消息类型。比如用户调用应用层有回传的接口时,就用这个消息类型。需要往南供给协议栈。 */
#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_thread这个线程帮忙执行这个回调函数,这个回调函数就能肆无忌惮地访问内核函数了。 */
TCPIP_MSG_CALLBACK_STATIC /* 静态的回调消息类型。 */
};
3.1、API消息
API消息是由用户线程发出,与内核进行交互。用户调用应用层接口,需要 tcpip_thread线程执行时,通过API消息告知 tcpip_thread线程执行。
API消息的数据结构如下所示:
struct api_msg包含3个字段:
- 描述连接信息的
struct netconn *conn;- 内核执行的结果
err_t err;- API接口数据结构
union msg;(``调用不同的API会使用不同的数据结构``)
cpp
/* IP addresses and port numbers are expected to be in the same byte order as in the corresponding pcb. */
/* 这个数据结构包含了在另一个线程上下文中执行netconn函数所需的所有内容(主要用于处理tcpip_thread上下文中的netconn以确保线程安全)。 */
struct api_msg {
/* 当前需要执行的API对应的连接 */
struct netconn *conn;
/* 在tcpip_thread中执行的函数的返回值 */
err_t err;
/* 用户调用不同的API,会使用不同的数据结构 */
union {
/* used for lwip_netconn_do_send */
struct netbuf *b;
/** used for lwip_netconn_do_newconn */
struct {
u8_t proto;
} n;
/** used for lwip_netconn_do_bind and lwip_netconn_do_connect */
struct {
API_MSG_M_DEF_C(ip_addr_t, ipaddr);
u16_t port;
u8_t if_idx;
} bc;
/** used for lwip_netconn_do_getaddr */
struct {
ip_addr_t API_MSG_M_DEF(ipaddr);
u16_t API_MSG_M_DEF(port);
u8_t local;
} ad;
/** used for lwip_netconn_do_write */
struct {
/** current vector to write */
const struct netvector *vector;
/** number of unwritten vectors */
u16_t vector_cnt;
/** offset into current vector */
size_t vector_off;
/** total length across vectors */
size_t len;
/** offset into total length/output of bytes written when err == ERR_OK */
size_t offset;
u8_t apiflags;
#if LWIP_SO_SNDTIMEO
u32_t time_started;
#endif /* LWIP_SO_SNDTIMEO */
} w;
/* used for lwip_netconn_do_recv */
struct {
size_t len;
} r;
#if LWIP_TCP
/* used for lwip_netconn_do_close (/shutdown) */
struct {
u8_t shut;
#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
u32_t time_started;
#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
u8_t polls_left;
#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
} sd;
#endif /* LWIP_TCP */
#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
/* used for lwip_netconn_do_join_leave_group */
struct {
API_MSG_M_DEF_C(ip_addr_t, multiaddr);
API_MSG_M_DEF_C(ip_addr_t, netif_addr);
u8_t if_idx;
enum netconn_igmp join_or_leave;
} jl;
#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
#if TCP_LISTEN_BACKLOG
struct {
u8_t backlog;
} lb;
#endif /* TCP_LISTEN_BACKLOG */
} msg;
#if LWIP_NETCONN_SEM_PER_THREAD
sys_sem_t* op_completed_sem;
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
};
3.2、数据包消息
消息类型为 TCPIP_MSG_INPKT。
数据包消息是底层网卡接收到数据后需要交给协议栈处理时,需要构造的消息。
其在 tcpip_inpkt()函数中构造。
主要将收到的数据包传递到 tcpip_thread线程执行。并告知要传入哪个内核函数处理。
cpp
/**
* 将接收到的数据包打包给tcpip_thread执行
*/
err_t
tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
{
#if LWIP_TCPIP_CORE_LOCKING_INPUT /* 开放了tcpip内核锁给input业务处理,就不需要外包到tcpip_thread处理 */
err_t ret;
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_inpkt: PACKET %p/%p\n", (void *)p, (void *)inp));
LOCK_TCPIP_CORE(); /* 获取tcpip内核锁 */
ret = input_fn(p, inp); /* 直接在本线程处理收到的数据包 */
UNLOCK_TCPIP_CORE(); /* 释放tcpip内核锁 */
return ret;
#else /* 如果没有开放tcpip内核锁给input业务处理,就需要外包到tcpip_thread处理 */
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_inpkt 分为两种实现:(下面指的 内核锁 其实是一个 mutex 互斥锁)
- 没有 tcpip 内核锁权限,就需要把 input 的业务通过消息转交给
tcpip_thread处理;- 如果有 tcpip 内核锁权限,那就获取 tcpip 内核锁,在本线程处理即可;(其中 sys_mbox_trypost 函数是一个 "消息队列" ,如果队列没满发送消息进去就是 ERR_OK,反之则反)
lwip数据包收包流图:

4、tcpip_thread线程
LwIP内核是作为操作系统的一个线程运行的,在协议栈初始化的时候就会创建 tcpip_thread线程。
该线程主要处理超时检查和接收各种消息进行处理。
tcpip内核锁是维护tcpip内核函数的原子性。
tcpip_thread内核线程和其它线程对内核tcpip的函数存在竞争关系。

4.1、lwip内核主线程
lwIP主线程。(这个线程独占地访问lwIP核心函数)
其他线程使用消息队列与该线程通信。
该线程还启动了所有的计时器,以确保它们在正确的线程上下文中运行。
cpp
static void
tcpip_thread(void *arg)
{
struct tcpip_msg *msg;
LWIP_UNUSED_ARG(arg);
/* tcpip内核上锁 */
LOCK_TCPIP_CORE();
if (tcpip_init_done != NULL) {
/* 执行用户插入的lwip内核初始化钩子函数 */
tcpip_init_done(tcpip_init_done_arg);
}
while (1) { /* MAIN Loop */
LWIP_TCPIP_THREAD_ALIVE(); /* 默认为空。这个可用于类似看门狗功能 */
/* 等待消息或超时事件 */
TCPIP_MBOX_FETCH(&tcpip_mbox, (void **)&msg);
if (msg == NULL) {
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: NULL\n"));
LWIP_ASSERT("tcpip_thread: invalid message", 0);
continue; /* 没有消息,继续等待 */
}
/* 处理收到的消息 */
tcpip_thread_handle_msg(msg);
}
}
4.2、等待消息/超时事件函数
cpp
#if !LWIP_TIMERS
/* wait for a message with timers disabled (e.g. pass a timer-check trigger into tcpip_thread) */
#define TCPIP_MBOX_FETCH(mbox, msg) sys_mbox_fetch(mbox, msg)
#else /* !LWIP_TIMERS */
/* wait for a message, timeouts are processed while waiting */
#define TCPIP_MBOX_FETCH(mbox, msg) tcpip_timeouts_mbox_fetch(mbox, msg)
static void
tcpip_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg)
{
u32_t sleeptime, res;
again:
LWIP_ASSERT_CORE_LOCKED();
sleeptime = sys_timeouts_sleeptime();
if (sleeptime == SYS_TIMEOUTS_SLEEPTIME_INFINITE) {
UNLOCK_TCPIP_CORE();
sys_arch_mbox_fetch(mbox, msg, 0);
LOCK_TCPIP_CORE();
return;
} else if (sleeptime == 0) {
sys_check_timeouts();
/* We try again to fetch a message from the mbox. */
goto again;
}
UNLOCK_TCPIP_CORE();
res = sys_arch_mbox_fetch(mbox, msg, sleeptime);
LOCK_TCPIP_CORE();
if (res == SYS_ARCH_TIMEOUT) {
/* If a SYS_ARCH_TIMEOUT value is returned, a timeout occurred
before a message could be fetched. */
sys_check_timeouts();
/* We try again to fetch a message from the mbox. */
goto again;
}
}
1、调用 sys_timeouts_sleeptime 获取下一次超时时间差;
若没有任何超时事件:
只调用 sys_arch_mbox_fetch 函数进行消息接收,此时可以无限等待邮箱消息;(所以这之前要解锁,不然会导致任务无法切换)
存在超时节点超时:
调用 sys_check_timeouts 函数进行超时回调处理;
存在超时节点,但没有超时:
等待邮箱,但最多等待 sleeptime 时间。等待超时,就执行超时回调,因为此时下个事件已经超时了;
4.3、tcpip_thread处理消息
其它线程需要发消息到 tcpip_thread()线程来处理内核操作。
如果开启了 tcpip 内核锁,客户端对lwip的操作也可以不用外包 tcpip_thread()线程处理。
cpp
/* 处理单个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
/* 没有开启tcpip内核锁,其它线程对lwip的操作需要外包到tcpip_thread线程操作 */
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); /* 执行API */
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); /* 执行API,并返回结果码 */
sys_sem_signal(msg->msg.api_call.sem); /* 释放信号量,告知用户线程执行API完毕 */
break;
case TCPIP_MSG_CALLBACK_STATIC_WAIT:
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK WAIT message %p\n", (void *)msg));
msg->msg.cb_wait.function(msg->msg.cb_wait.ctx); /* 执行回调 */
sys_sem_signal(msg->msg.cb_wait.sem); /* 释放信号量,告知插入回调的线程回调执行完毕 */
break;
#endif /* !LWIP_TCPIP_CORE_LOCKING */
#if !LWIP_TCPIP_CORE_LOCKING_INPUT
/* 没有开启LWIP_TCPIP_CORE_LOCKING_INPUT,所以tcpip_input()没有权限好的tcpip内核锁,只能外包回tcpip_tread线程处理 */
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); /* 处理完毕后释放这个pbuf的资源 */
}
memp_free(MEMP_TCPIP_MSG_INPKT, msg); /* 释放消息资源 */
break;
#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
/* 支持在tcpip_thread执行计时器功能 */
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;
}
}