typora-copy-images-to: upload
Lwip首先需要了解三个部分:
1.内存管理 (替代C库malloc的一系列操作)
2.网络接口 与实际的PHY芯片,MAC层之间的接口,以及操作系统的函数接口
3.数据包 特指pbuf结构体,数据处理以及上下层传递均建立在pbuf的基础上
了解了这三个部分之后,便可展开源码,以下梳理出其中的一些重要的函数,不涉及具体的网络协议,大概明白了数据传递的过程。
TCPIP_Init
初始化,很大部分代码都是绑定函数。
tcpip_init
创建 tcpip_thread 线程和线程所用邮箱 tcpip_mbox。在tcpip_thread 线程中根据底层传来的包类型进行不同的处理。
netif_add
注册网卡,进行网卡底层初始化,绑定底层输入输出函数,绑定底层传向上层的函数netif->input,调用ethernetif_init---->low_level_init中创建ethernetif_input线程,获取中断释放的信号量s_xSemaphore,获取成功后执行low_level_input返回一个pbuf对象p,再调用 netif->input(p, netif) 。该函数指针为netif_add 传入的参数。 netif为全局变量网卡gnetif ,input函数为 tcpip_input。在tcpip_input中将ethernet_input()函数作为结构体的一部分传递给内核,然后内核接收到这个数据包就调用该函数
tcpip_input
这个函数中调用tcpip_inpkt函数,将底层传入的pbuf、netif、ethernet_input()函数打包成tcpip_msg类型的的包,将包发送至 tcpip_mbox 邮箱,在 tcpip_thread 线程中进行处理。包类型赋值固定值TCPIP_MSG_INPKT宏,表示传进来的数据包。tcpip_thread 线程中实际调用 tcpip_thread_handle_msg(msg),然后根据包类型(TCPIP_MSG_INPKT)调用 msg->msg.inp.input_fn 指针,而该指针正是在 tcpip_input 函数中指定的 ethernet_input 或 ip_input
err_t tcpip_input(struct pbuf *p, struct netif *inp)
{
#if LWIP_ETHERNET
if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
return tcpip_inpkt(p, inp, ethernet_input);//在这里面到宝成msg发送邮箱
} else
#endif /* LWIP_ETHERNET */
return tcpip_inpkt(p, inp, ip_input);
}
ip_input 就是 ip4_input 的宏。实际上,在 ethernet_input 也可能会调用 ip4_input ,用于处理IP协议。除此之外,ethernet_input 还可以处理ARP协议(调用 etharp_input),总之tcpip_thread_handle_msg就是一个case语句
tcpip_thread线程
static void tcpip_thread(void *arg)
{
struct tcpip_msg *msg;
LWIP_UNUSED_ARG(arg);
LWIP_MARK_TCPIP_THREAD();
LOCK_TCPIP_CORE();
if (tcpip_init_done != NULL) {
tcpip_init_done(tcpip_init_done_arg);
}
while (1) { /* MAIN Loop */
LWIP_TCPIP_THREAD_ALIVE();
/* wait for a message, timeouts are processed while waiting */
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);
}
}
真正的数据处理是 tcpip_thread_handle_msg(msg) 根据msg的类型执行对应的处理函数。
整个流程见下图
超时处理
两个基本结构体
struct lwip_cyclic_timer {
u32_t interval_ms;
lwip_cyclic_timer_handler handler;
};
struct sys_timeo {
struct sys_timeo *next;
u32_t time;//到达超时时刻的绝对时间
sys_timeout_handler h;
void *arg;
};
lwip_cyclic_timers[]
lwip_cyclic_timers[] 数组,根据宏使能定义
const struct lwip_cyclic_timer lwip_cyclic_timers[] = {
{TCP_TMR_INTERVAL, HANDLER(tcp_tmr)},
{IP_TMR_INTERVAL, HANDLER(ip_reass_tmr)},
{ARP_TMR_INTERVAL, HANDLER(etharp_tmr)},
{超时时间毫秒数,各自协议的超时回调函数},
...
};
sys_timeouts_init
遍历 lwip_cyclic_timers[] 数组,调用 sys_timeout 函数注册超时时间、事件函数、参数。时间为数组各元素的第一个成员interval_ms,超时函数为 lwip_cyclic_timer(*arg), 参数为 lwip_cyclic_timers[] 数组各元素的地址
void sys_timeouts_init(void)
{
size_t i;
for (i = (LWIP_TCP ? 1 : 0); i < LWIP_ARRAYSIZE(lwip_cyclic_timers); i++)
{
sys_timeout(lwip_cyclic_timers[i].interval_ms, lwip_cyclic_timer, LWIP_CONST_CAST(void *, &lwip_cyclic_timers[i]));
}
}
sys_timeout
本函数中,设置下一次达到超时条件的时标 next_timeout_time ,作为绝对时间传入 sys_timeout_abs 函数。实际上是调用sys_timeout_abs()函数,这里的 handle 为 lwip_cyclic_timer(void *arg)函数
void sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg)
{
u32_t next_timeout_time;
LWIP_ASSERT_CORE_LOCKED();
next_timeout_time = (u32_t)(sys_now() + msecs);
sys_timeout_abs(next_timeout_time, handler, arg);
}
sys_timeout_abs
本函数中,处理三个 struct sys_timeo类型的结构体(两个局部变量*timeout, *t。一个全局变量next_timeout)。next_timeout是一个全局变量,初始为空链表。
static struct sys_timeo *next_timeout;//全局链表
传入的的 handle 为 lwip_cyclic_timer(void *arg)函数,传入的arg为lwip_cyclic_timers[] 数组的各个元素。使用初始化将 lwip_cyclic_timers[] 数组的每个元素的地址作为arg,与struct sys_timeo结构绑定,挂到全局链表 next_timeout 上
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
timeout->next = NULL;
timeout->h = handler;//传入的的 handle 为 lwip_cyclic_timer(void *arg)函数
timeout->arg = arg;
timeout->time = abs_time;//到达超时时刻的绝对时间
if (next_timeout == NULL) {
next_timeout = timeout;//将结构体插入空链表
return;
}
//当前超时对象与链表首部作比较
if (TIME_LESS_THAN(timeout->time, next_timeout->time)) {//无符号数作比较,参数1是否比参数2小
timeout->next = next_timeout;//超时时间大的排序到next
next_timeout = timeout;//超时时间小的地址赋值给链表首地址
} else {
//首部不是timeout,即timeout不是最小时间的情况
for (t = next_timeout; t != NULL; t = t->next) {//遍历链表
if ((t->next == NULL) || TIME_LESS_THAN(timeout->time, t->next->time))
{//如果当前超时对象timeout 比链表中某个对象->next 时间小
timeout->next = t->next;
t->next = timeout;
break;
}
}
}
}
例如按顺序插入 1 3 2
1. next_timeout为空 ,1插到首部
2. 插入3,满足条件1的next==NULL,将NULL赋值给3的next,将3变为1的next.此时链表为“1 3 空”
3. 插入2,满足条件2比1的next的时间(3)小,将1的下一个(3)变为2的下一个,将2变为1的下一个。此时链表为“1 2 3 空”
lwip_cyclic_time函数
每个sys_timeo结构体中的arg成员变量记录着lwip_cyclic_timers【】数组各元素,在初始化的时候将他们注册到 lwip_cyclic_timer()函数中,每次在处理回调函数之后,就调用sys_timeout_abs()函数将其重新注册到超时链表中
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;
cyclic->handler();//这里的handler为数组元素各自协议的超时回调函数
now = sys_now();
//重新设置下一次超时的绝对时间,时间升序排列
next_timeout_time = (u32_t)(current_timeout_due_time + cyclic->interval_ms);
if (TIME_LESS_THAN(next_timeout_time, now)) {
sys_timeout_abs((u32_t)(now + cyclic->interval_ms), lwip_cyclic_timer, arg);
} else {
sys_timeout_abs(next_timeout_time, lwip_cyclic_timer, arg);
}
}
超时检查
在 tcpip_thread 线程中,读取邮箱
TCPIP_MBOX_FETCH(&tcpip_mbox, (void **)&msg);//读邮箱
TCPIP_MBOX_FETCH是一个宏,真正调用的是 tcpip_timeouts_mbox_fetch
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();//得到距离事件超时的时间并保存在sleeptime变量中
if (sleeptime == SYS_TIMEOUTS_SLEEPTIME_INFINITE)//当前无超时事件,sleeptime为ffffffff,那只需一直等邮箱
{
UNLOCK_TCPIP_CORE();
sys_arch_mbox_fetch(mbox, msg, 0);//0表示无需等别的事,立即查邮箱
LOCK_TCPIP_CORE();
return;
} else if (sleeptime == 0) {//如果sleeptime为0表示发生超时了,那就调用sys_check_timeouts()去检查一下到底是哪个事件发生超时并且去处理其超时回调函数
sys_check_timeouts();
goto again;
}
//有超时事件但是时间未到
UNLOCK_TCPIP_CORE();
//等待tcpip_mbox的消息的同时就去处理超时事件
//如果接收到消息,并且超时时间还没到,那就去处理tcpip_mbox的消息,然后再回来重新计算等待时间sleeptime
res = sys_arch_mbox_fetch(mbox, msg, sleeptime);//返回等待时间
LOCK_TCPIP_CORE();
if (res == SYS_ARCH_TIMEOUT) {//ffffffff
sys_check_timeouts();
goto again;
}
}
发生超时时实际上是调用 sys_check_timeouts ,看看是哪一个超时
void sys_check_timeouts(void)
{
u32_t now;
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;//这里的handler为lwip_cyclic_timer函数
arg = tmptimeout->arg;//arg则是那个数组元素
current_timeout_due_time = tmptimeout->time;
memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
if (handler != NULL) {
handler(arg);//调用超时回调函数 lwip_cyclic_timer(void *arg),在内部调用真正的超时函数,然后调整超时链表
}
LWIP_TCPIP_THREAD_ALIVE();
} while (1);
}