目录
TCP/IP 是一种数据通信机制,因此,协议栈的实现本质上就是对数据包进行处理,为了实现高效的效率,LwIP 数据包管理要提供一种高效处理的机制。协议栈各层能对数据包进行灵活的处理,同时减少数据在各层间传递时的时间与空间开销,这是提高协议栈工作效率的关键点。在BSD 的实现中,一个描述数据包的结构体叫做mbuf,同样的 在 LwIP中,也有个类似的结构,称之为 pbuf,本章所有知识点将围绕 pbuf 而展开。
1. TCP/IP 协议分层
在标准的TCP/IP 协议栈中,各层之间都是一个独立的模块,它有着很清晰的层次结构,每一层只负责完成该层的处理,不会越界到其他层次去读写数据。
LwIP 只是一个轻量级TCP/IP 协议栈,它只是一个较完整的TCP/IP 协议,多应用在嵌入式领域中,由于处理器的性能有限,LwIP 并没有采用很明确的分层结构,它假设各层之间的部分数据和结构体和实现原理在其他层是可见的,简单来说就传输层知道IP 层是如何封装数据、传递数据的,IP 层知道链路层是怎么封装数据的等等。
为什么要模糊分层的处理?简单来说就是为了提高效率,例如链路层完成数据包在物理线路上传输的封装;IP 层完成数据包的选择和路由,负责将数据包发送到目标主机;传输层负责根据IP 地址将数据包传输到指定主机,端口号识别一台主机的线程,向不同的应用层递交数据;但是,如果按照标准的TCP/IP 协议栈这种严格的分层思想,在数据传输的时候就需要层层拷贝,因为各层之间的内存都不是共用的,在链路层递交到IP 层需要拷贝,在IP 层递交到传输层需要拷贝,反之亦然,这样子每当收到或者发送一个数据的时候都要CPU 去拷贝数据,这个效率就太慢了。
所以LwIP 假设各层之间的资源都是共用的,各层之间的实现方式也是已知的,那么在IP 层往传输层递交数据的时候,链路层往IP 层递交数据的时候就无需再次拷贝,直接操作协议栈中属于其他层次的字段,得到相应的信息,然后直接读取传递的数据即可,这样处理的方式就无需拷贝,各个层次之间存在交叉存取数据的现象,既节省系统的空间也节省处理的时间,而且更加灵活。
在小型嵌入式设备中,LwIP 与用户程序之间通常没有太严格的分层结构,这种方式允许用户处理数据与内核之间变得更加宽松。LwIP 假设用户完全了解协议栈内部的数据处理机制,用户程序可以直接访问协议栈内部各层的数据包,可以让协议栈与用户使用同样的内存区域,允许用户直接对这片区域进行读写操作,这样子就很好地避免了拷贝的现象,当然这样子的做法也有缺陷,取决于用户对协议栈处理过程的了解程度,因为数据是公共的,如果处理不正确那就让协议栈也没法正常工作。
当然,除了标准的TCP/IP 协议,还存在很多其他的TCP/IP 协议,即使这些协议栈内部存在着模糊分层、交叉存取现象,但是对协议栈外部的应用层则保持着明显的分层结构,在操作系统中,TCP/IP 协议栈往往被设计为内核代码的一部分,用户可以的函数仅仅是协议栈为用户提供的那些,或者直接完全封装起来,用户的操作类似于读写文件的方式进行(如BSD Socket),这样子用户就无法避免数据的拷贝,在数据发送的时候,用户数据必须从用户区域拷贝到协议栈内部,在数据接收的时候,协议栈内部数据也将被拷贝到用户区域。
简而言之,对于资源有限的嵌入式开发,平衡软件设计层次之间的耦合度和资源消耗。
2. lwIP的线程模型
线程模型可以理解为协议栈的实现被划分在多个线程之中,如让协议栈的各个层次都独立成为一个线程,在这种模式下,各个层次都有严格分层结构,各个层次的提供的API接口也是分层清晰的,这样固然使得编程更加简便、高效、灵活。但对于嵌入式设备,严格的分层,势必引起更多数据的传输、拷贝及线程切换,这是很大的开销。如从链路层到应用层,数据要经历数次的拷贝和线程切换,这样使得协议栈的效率低下。
其次,协议栈与操作系统融合,成为操作系统的一部分,这样子用户线程与协议栈内核之间都是通过操作系统提供的函数来实现的,这种情况让协议栈各层之间与用户线程就没有很严格的分层结构,各层之间能交叉存取,从而提高效率。
LwIP 采用了另一种方式,让协议栈内核与操作系统相互隔离,协议栈仅仅作为操作系统的一个独立线程存在,用户程序能驻留在协议栈内部,协议栈通过回调函数实现用户与协议栈之间的数据交互;也可让用户程序单独实现一个线程,与协议栈使用系统的信号量和邮箱等 IPC 通信机制联系起来,进行数据的交互。
当使用第一种通过回调函数进行交互情况的时候,也就是我们所说的RAW API 编程。当通过操作系统IPC通信机制的时候,就是另外两种API 编程,即NETCONN API 和Socket API。当然这样子既有优点也有缺点,优点就是能在任何的操作系统中移植,缺点就是受到操作系统的影响。
因为即使LwIP 作为一个独立的线程,也是需要借助操作系统进行调度的,因此,协议栈的响应的实时性会有一定影响,并且建议设置LwIP 线程的优先级为最高优先级。
3. pbuf 结构体说明
pbuf 就是一个描述协议栈中数据包的数据结构,LwIP 中在pbuf.c 和pubf.h 实现了协议栈数据包管理的所有函数与数据结构。
/** Main packet buffer struct */
struct pbuf {
/** next pbuf in singly linked pbuf chain */
struct pbuf *next;
/** pointer to the actual data in the buffer */
void *payload;
/**
* total length of this buffer and all next buffers in chain
* belonging to the same packet.
*
* For non-queue packet chains this is the invariant:
* p->tot_len == p->len + (p->next? p->next->tot_len: 0)
*/
u16_t tot_len;
/** length of this buffer */
u16_t len;
/** a bit field indicating pbuf type and allocation sources
(see PBUF_TYPE_FLAG_*, PBUF_ALLOC_FLAG_* and PBUF_TYPE_ALLOC_SRC_MASK)
*/
u8_t type_internal;
/** misc flags */
u8_t flags;
/**
* the reference count always equals the number of pointers
* that refer to this pbuf. This can be pointers from an application,
* the stack itself, or pbuf->next pointers from a chain.
*/
LWIP_PBUF_REF_T ref;
/** For incoming packets, this contains the input netif's index */
u8_t