本章节将从LwIP数据收发的角度,剖析LwIP的内部机制。

1、网络数据的接收机制

从上图我们看到LwIP是如何接收网络数据包的。

首先,协议栈在初始化时,调用netif_add()接口向协议栈添加网卡。

*/netif_add 函数接口 */
struct netif *
netif_add(struct netif *netif,
          const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,
          void *state, netif_init_fn init, netif_input_fn input)

这个接口我们需要指定

  • 网卡netif
  • 网卡所设置的ip地址
  • 子网掩码
  • 网关
  • 向网卡传递的信息state
  • 网卡驱动的初始化函数,需要底层驱动实现
  • 网卡接收数据时,向协议栈上报的接口,通常是tcpip_input

netif_add紧接着会立刻调用我们刚刚指定的网卡驱动的初始化函数,在初始化函数中,我们创建一个数据接收线程以及一个用于通知数据接收线程前去接收数据的中断信号量。

针对协议栈一侧的初始化,通过tcpip_init()来进行。这个函数将会创建tcpip线程以及一个接收其它线程消息的邮箱。

当数据到达时将触发网卡接收中断,此时中断服务函数释放信号量,数据接收线程获取到信号量从网卡的缓存区中取走数据封装成pbuf类型,同时将数据通过tcpip_input()向协议栈传递。如何传递呢?tcpip_input()将数据pbuf、接收网卡netif、以及数据的处理接口(通常是ethernetif_input())这三个参数封装成一个类型为TCPIP_MSG_INPKT的邮箱数据MSG向一个邮箱进行投递。此时,对数据的后续处理工作将交给协议栈来处理。

协议栈线程拿到了邮箱消息,开始针对这个邮箱数据MSG进行不同的处理。当发现邮箱数据类型是TCPIP_MSG_INPKT时,调用ethernetif_input对数据进行处理

ethernetif_input这个接口,会通过以太网的头部信息,判断数据的类型(TYPE)。如果是IPV4(0x0800)则调用ip4_input()来处理这段数据;如果是ARP(0X0806),则调用etharp_input()来处理这段数据。当然还有可能是IPV6或者其它的数据包,都有对应的接口来处理。

至此,数据就从链路到达协议栈了,我们可以发现这个过程中,底层驱动的线程和网络协议栈线程是异步进行的,这种方式可以最大限度地提高系统的吞吐率和CPU利用率。

2、网络数据的发送

协议栈通过netif的linkoutput()进行数据发送,这个发送函数需要我们在底层驱动中实现。具体的发送机制,将在后面分析不同的网络协议时向大家介绍。