Lwip协议栈移植过程
1、将LwIP源码添加到工程中,只需要将LwIP源码中的src文件文件夹添加进去即可。
2、移植头文件:想LwIP跑起来,还需一些头文件的支持,分别是lwipopts.h、cc.h、pref.h等。
3、移植网卡驱动:ethernetif.c文件就是存放这些底层驱动函数的,LwIP的contrib包中就包含这个文件的模板,我们需要在这个基础上修改。对底层驱动进行编写,网卡的初始化、收发数据接收数据等。
4、 LwIP时基:LwIP也是一个内核,与操作系统一样,也是由时基驱动的,LwIP作者为了能让内核正常运行,也引入了一个时钟来驱动,这样子可以处理内核中各种定时事件,如ARP定时、TCP定时等。一般采用SysTick作为LwIP的时基定时器。
5、协议栈初始化:想要使用LwIP,那就必须先将协议栈初始化,我们就创建一个函数,在函数中初始化协议栈,注册网卡,设置主机的IP地址、子网掩码、网关地址等。
LWIP带操作系统的优势
- LwIP不仅能在裸机上运行,也能在操作系统环境下运行,而且在操作系统环境下,用户能使用NETCONN API 与Socket API编程,相比RAW API编程会更加简便。操作系统环境下,这意味着多线程环境,一般来说LwIP作为一个独立的处理线程运行,用户程序也独立为一个或多个线程,这样子在操作系统中就相互独立开,并且借助操作系统的IPC通信机制,更好地实现功能的需求。
- LwIP设计者在设计的时候就提供一套与操作系统相关的接口,由用户根据操作系统的不同进行移植,这样子就能降低耦合度,让LwIP内核不受其运行的环境影响。
三种API的区别
- 在有操作系统的环境中,如果使用RAW/Callback API,用户的应用程序就以回调函数的形式成为了内核代码的一部分,用户应用程序和内核程序会处于同一个线程之中,这就省去了任务间通信和切换任务的开销了。
- (1)可以在没有操作系统的环境中使用。
- (2)在有操作系统的环境中使用它,对比另外两种API,可以提高应用程序的效率、节省内存开销。
- (3)开发复杂,可读写差;应用程序和内核代码运行制约。
- NETCONN API是基于操作系统的IPC机制(即信号量和邮箱机制)实现的,它的设计将LwIP内核代码和网络应用程序分离成了独立的线程。如此一来,LwIP内核线程就只负责数据包的TCP/IP封装和拆封,而不用进行数据的应用层处理,大大提高了系统对网络数据包的处理效率。
- 在操作系统环境中,LwIP内核会被实现为一个独立线程,名为
tcpip_thread
,使用NETCONN API或者Socket API的应用程序处在不同的线程中 - (1)相较于RAW/Callback API,NETCONN API简化了编程工作,使用户可以按照操作文件的方式来操作网络连接。但是,内核程序和网络应用程序之间的数据包传递,需要依靠操作系统的信号量和邮箱机制完成,这需要耗费更多的时间和内存,另外还要加上任务切换的时间开销,效率较低。
- (2)相较于Socket API,NETCONN API避免了内核程序和网络应用程序之间的数据拷贝,提高了数据递交的效率。但是,NETCONN API的易用性不如Socket API好,它需要用户对LwIP内核所使用数据结构有一定的了解。
- 在操作系统环境中,LwIP内核会被实现为一个独立线程,名为
- socket对网络连接进行了高级的抽象,使得用户可以像操作文件一样操作网络连接 。
- 相较于NETCONN API, Socket API具有更好的易用性。使用Socket API编写的程序可读性好,便于维护,也便于移植到其它的系统中。Socket API在内核程序和应用程序之间存在数据的拷贝,这会降低数据递交的效率。另外,LwIP的Socket API是基于NETCONN API实现的,所以效率上相较前者要打个折扣。
网络中不同层对应的协议
- 应用层:
- HTTP:超文本传输协议
- HTTPS:超文本传输安全协议
- FTP:文件传输协议
- SMTP:简单文件传输协议
- DNS: 域名解析系统
- DHCP:动态主机分配协议
- 传输层:
- TCP:传输控制协议
- UDP:用户数据协议
- 网络层:
- IP:网际协议
- ICMP:网际控制报文协议
- IGMP:网络组管理协议
- ARP:地址解析协议
- 数据链路层:
- PPP:点到点协议
LWIP网络协议栈的移植
- src中包含了lwip的源代码:
api文件夹:文件夹中存放NETCONN API和Socket API,只有在有操作系统时才会被编译。
apps文件夹:文件夹中创的是;应用层的源文件,如HTTP、mdns(多播域名系统)、mqtt(消息队列遥测传输)、snmp(简单网络管理协议)、sntp(简单网络时间协议)、tftp(简单文件传输协议)
core文件夹:LWIP内存源码ipv4(6)文件件:与ipv4相关的源文件。其中还包括一些受ip协议影响的源文件如:DHCP、ARP、ICMP、IGMP等
实现各种功能的源文件:如实现TCP、UDP、timeouts.c内核超时处理、pbuf.c网络数据包的各种操作、netif.c文件实现了网卡的操作、mem.c文件实现了动态内存池管理机制、memp.c文件实现了静态内存堆管理机制、dns.c文件实现了域名解析的功能、inet_chksum.c文件提供了LwIP所需的校验和功能include文件夹:lwip所有模块对应的头文件
netif文件夹:与网卡移植相关的文件,这些文件为移植的网卡提供了模板,我在直接在这些模板上完成底层驱动代码
以太网的功能框图
- MII 和 RMII 接口用于 MAC数据包传输, ETH 还集成了站管理接口(SMI)接口专门用于与外部 PHY 通信,用于访问PHY 芯片寄存器。
- ETH 有专用的 DMA 控制器,它通过 AHB 主从接口与内核和存储器相连, AHB 主接口用于控制数据传输,而 AHB 从接口用于访问“控制与状态寄存器” (CSR)空间。在进行数据发送是,先将数据从存储器以 DMA 传输到发送 TX FIFO 进行缓冲,然后由 MAC 内核发送; 接收数据时, RX FIFO 先接收以太网数据帧,再由 DMA 传输至存储器。
netif结构体(lwip网卡驱动实现)
- 网络接口又可以称之为网卡,LwIP使用一个数据结构——netif来描述一个网卡,LwIP提供统一的接口, 但是底层的实现需要用户自己去完成。LwIP中的 ethernetif.c文件即为底层接口的驱动的模版,用户为自己的网络设备实现驱动时应参照此模块做修改。
- LwIP提供统一的接口, 但是底层的实现需要用户自己去完成,比如网卡的初始化,网卡的收发数据,当LwIP底层得到了网络的数据之后, 才会传入内核中去处理。LwIP中的 ethernetif.c文件即为底层接口的驱动的模版,用户为自己的网络设备实现驱动时应参照此模块做修改。
- netif的简单使用:
- (1):设置主机IP地址、子网掩码、网关等字段信息。
- (2):完成网卡的初始化
- (3):将网卡插入到网卡链表中
- (4):将网卡设置为默认网卡
- 与netif相关的底层函数:
//与netif相关的底层函数
static void low_level_init(struct netif *netif);
static err_t low_level_output(struct netif *netif, struct pbuf *p);
static struct pbuf * low_level_input(struct netif *netif);//负责将数据接收填充到pbuf中
//除此之外,还有两个函数也与网卡与关系
err_t ethernetif_init(struct netif *netif);
void ethernetif_input(void *pParams);//负责将pbuf递交给上层
LWIP与操作系统的接口
- LwIP设计者在设计的时候就提供一套与操作系统相关的接口,由用户根据操作系统的不同进行移植,这样子就能降低耦合度,让LwIP内核不受其运行的环境影响。
- lwipopts.h文件修改一下, 该文件最重要的宏定义就是NO_SYS,我们把它定义为0就表示使用操作系统
- 操作系统环境下, LwIP移植的核心就是编写与操作系统相关的接口文件sys_arch.c和sys_arch.h,用户必须根据操作系统的功能为协议栈提供相应的接口,如邮箱、信号量、互斥量等,这些IPC通信机制是保证内核与上层API接口通信的基本保障,也是内核实现管理的继承,同时在sys.h文件中声明了用户需要实现的所有函数框架。
- 在操作系统环境下,LwIP会作为一个线程运行,线程的名字叫tcpip_thread
- 使用了操作系统的话, 我们一般将接收数据函数独立成为一个网卡接收线程,这样子在收到数据的时候才去处理数据
- 当然,我们也能将发送函数独立成一个线程
LWIP的内存管理
- 动态内存池分配策略(固定大小的内存块分配策略):系统将内存分成固定大小的内存块,用多个单链表的连接起来,分配时从头部取出一个内存块,释放时将内存块插入到链表头部。
- 动态内存堆分配策略:
- 内存池可由内存堆实现,反之,内存堆也可以由内存池实现。
- 动态内存堆分配策略本质是对事先定义好的内存块进行合理有效的组织和管理,内存分配的策略采用首次拟合方式,一旦找到比用户要求大的空闲块,就从中切割,并把剩余的部分返回到动态内存堆中。这种分配策略有点是内存浪费小,适合小内存管理,缺点是如果频繁分配和释放,可能导致大量的内存碎片
计算机网络为设么要分层
- 各层之间独立:某一层不需要知道他的上一层和下一层是如何实现的,只需要知道他的上下层提供的接口即可。每一层实现相当独立的功能,可以将复杂的问题简单化。
- 灵活性好:当某一层发生变化时,只要层间接口保持不变,其他层不需变动。
- 易于实现和维护:调试一个庞大和复杂的系统时变的相对容易,因为可以分别对没有子系统进行调试。
网络数据包Pbuf
- 协议栈的实现本质就是对数据包进行处理。为了实现高效的处理机制,LWIP提供了pbuf数据结构。
- LWIP与用户程序直接没有太严格的分层结构,这种方式运行用户处理数据与内核之间更加宽松。用户程序可以直接访问协议栈内部各层的数据包。允许用户和协议栈对同一片区域进行读写,避免了数据拷贝。
- pbuf就是一个描述协议栈中数据包的数据结构:
struct pbuf
{
struct pbuf *next; //指向下一个pbuf
void *payload; //数据字段
u16_t tot_len; //记录当前Pbuf和后续pbuf中数据的总长度
u16_t len; //记录当前pbuf中数据的长度
u8_t type_internal; //指明四种pbuf中的哪一种
u8_t flags; //一般初始化为0
LWIP_PBUF_REF_T ref; //pbuf被引用的次数
u8_t if_idx; //记录当前pbuf的索引
};
pbuf的类型PBUF_RAM 、PBUF_POOL 、PBUF_ROM 、PBUF_REF、
- PBUF_RAM 与PBUF_POOL 的内部结构相似,都是结构体首部和数据段存放到一块连续的空间。但PBUF_RAM采用内存堆分配策略,PBUF_POOL 采用内存池分配策略。
- PBUF_ROM 、PBUF_REF结构类似,是将结构体首部和数据区域分开存储的。
pbuf_alloc()
- 数据包申请函数pbuf_alloc()在系统中的许多地方都会用到,例如在网卡接收数据时,需要申请一个数据包, 然后将网卡中的数据填入数据包中;在发送数据的时候,协议栈会申请一个pbuf数据包, 并将即将发送的数据装入到pbuf中的数据区域,同时相关的协议首部信息也会被填入到pbuf中的layer区域内。数据包pbuf的类型和数据包在哪一层被申请。
LWIP将数据向用户线程传递的过程
- 1、接收线程系统调用lowlevel_input()函数将数据接收并存入pbuf中。
- 2、接收线程根据接收数据类型的不同构造不同的消息。
- 3、接收线程调用tcpip_inpkt()实际底层是调用ethernet_input()将消息投递到邮箱中。
- 4、tcpip_thread线程获取邮箱消息并处理数据包。
LWIP数据收发流程
连接链路层和网络层的纽带:以太网数据包接收进程tcpip_thread
1.链路层
- 主机A和主机B进行通信时,ARP协议会将主机B的IP地址解析成主机B的MAC地址,工作流程如下:
第一步:主机A先检查自己的ARP缓冲,看是否存在主机B匹配的MAC地址,如果没有,就会向外广播一个ARP请求包。
第二步:其他主机收到后,发现请求的IP地址与自己的IP地址不匹配,则丢弃ARP请求。
第三步:主机B确定ARP请求中的IP地址与自己的IP地址匹配,则将主机A的IP地址和MAC地址映射到本地ARP缓存中。
第四步:主机B将包含其MAC地址的ARP回复发回给主机A。
第五步:主机A收到从主机B发来的ARP回复时,会将主机B的IP地址和MAC地址映射更新到本地ARP缓存中。主机B的MAC地址一旦确定,主机A就可以向主机B发送IP通信了。
2.网络层
- 网络层的协议包括IP、ARP、ICMP、IGMP协议
负责的任务:
- IP数据包的处理:网络层负责处理接收到的IP数据包,包括基本的分片和重组功能。
- 协议封装:网络层负责将传输层的TCP/UDP数据封装成IP数据包,或者将收到的IP数据包传递给传输层。
- ARP解析:对于IPv4,网络层需要通过ARP协议解析目标IP地址的MAC地址。
- 地址分配:网络层可以配合DHCP客户端动态获取IP地址,也可以静态配置IP地址。
- 多播和广播:网络层支持IGMP和MLD协议,处理IPv4和IPv6的多播数据 。。。。。。