计算机网络-9-LwIP及其网络编程应用实例ppt课件_第1页
计算机网络-9-LwIP及其网络编程应用实例ppt课件_第2页
计算机网络-9-LwIP及其网络编程应用实例ppt课件_第3页
计算机网络-9-LwIP及其网络编程应用实例ppt课件_第4页
计算机网络-9-LwIP及其网络编程应用实例ppt课件_第5页
已阅读5页,还剩102页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第九章LwIP及其网络编程应用实例 LwIP介绍 LwIP LightWeightInternetProtocol 是瑞典计算机科学院 SwedishInstituteofComputerScience 的AdamDunkels等人开发的一套用于嵌入式系统的开源TCP IP协议栈 LwIP的含义是轻型IP协议 其实现的重点是在保持TCP协议主要功能的基础上减少对RAM的占用 这使得LwIP协议栈非常适合在小型嵌入式系统中使用 LwIP介绍 LwIP的版本较多 较新的版本通常完善或增加了LwIP的功能 LwIP有如下特点 IP 支持多网络接口下的IP转发ARP 支持ARP协议ICMP 支持ICMP协议UDP 支持UDP协议TCP 支持TCP协议 包括拥塞控制 RTT估算和快速恢复 快速重传RawAPI 提供专门的内部回调函数 以提高应用性能SocketAPI 可选的Berkeley likesocketAPILwIP的较新版本还提供对以下功能或协议的支持 IPfragment IP分片DNS 域名解析SNMP 简单网络管理协议DHCP 动态主机配置协议PPP 点对点协议IPv6 LwIP源码的文件组织 LwIP文件目录的组织结构如图所示 其源代码全部位于目录src下 src目录下一般有5个子目录LwIP提供的api子目录 core子目录 include子目录和netif子目录需用户自己创建的arch目录 LwIP源码的文件组织 每个子目录包含的某一类相关的文件 简要说明如下 api目录应用程序接口文件 arch目录与硬件和OS有关的文件 包括网络驱动 移植需要修改的文件 core目录LwIP的核心代码 包括ICMP IP UDP TCP等协议的实现等 include目录LwIP的包含文件 netif目录ARP协议和LwIP网络设备驱动程序的模板 提供了网络接口驱动程序的基本框架 LwIP的软件体系结构 LwIP的协议层次 LwIP也是以4层TCP IP模型为参照来实现TCP IP协议族的 每一个协议作为一个模块被实现 同时还提供了几个函数作为协议的入口点 LwIP并没有严格地按照分层的方式实现协议族 实际上LwIP使用的是一种比较松散的通讯机制 通过共享内存的方式实现应用层与底层协议族之间的通讯 LwIP拥有独特的缓冲机制 使得各层次可以更加有效的重复使用缓冲区 LwIP尽量避免内存复制 避免了内存复制产生的性能损失 LwIP的软件体系结构 与LwIP的协议层次相匹配 LwIP采用模块化设计的方法实现 TCP IP协议的实现模块如ARP IP ICMP UDP TCP等许多相关支持模块 这些支持模块包括操作系统模拟层 缓冲与内存管理子系统 网络接口函数等 LwIP的进程模型 TCP IP协议族的进程模型指的是采用何种方法把系统分成不同的进程 常见的进程模型有两种 每一个协议作为一个独立的进程协议栈作为一个内核只占据一个进程 第一种模型必须符合协议的每一层 协议层之间通过指定的方式进行通讯 优点较明显 即每一种协议都可以独立参与到系统运行中 其实现的代码也比较简单 整个协议栈的层次脉络清晰 便于理解和调试 缺点也是显而易见的 即数据跨层传递时不得不产生进程切换以及内存复制 这一缺点极大影响了系统的整体性能 尤其对于嵌入式系统来说更是不能忍受的 第二种模型将协议栈驻留在操作系统内核中 应用程序通过系统调用与协议栈进行通讯 这种设计可以使用交叉协议分层技术 各层协议不必严格划分 这种进程模型的缺点是层次不清 给理解增加了难度 LwIP的进程模型 LwIP则采用一种比较灵活的设计方法 它可以将所有的协议驻留在一个进程 以便独立于操作系统内核之外 应用程序既可以驻留在LwIP的进程中 也可以使用一个单独的进程 它也可以根据协议层次结构创建多个进程 但各个进程之间只传送尽可能少的必要信息 而没有引入额外的内存复制LwIP在协议层之间切换时 一般只传递数据缓冲区的地址 让需要处理数据的协议层自己去提取 LwIP的函数调用关系 为了尽量避免不必要的内存复制 LwIP更多的是采用一种基于回调函数的设计方法 当数据需要处理或跨层传递时 通常是通过调用事先已定义好的回调函数来完成有关操作 优点是大大提高了LwIP的整体性能 缺点是使得LwIP的整个软件体系显得略微复杂 尤其是函数之间的调用关系更为繁琐 为了理清LwIP的函数调用关系 从两个不同的方向对这一问题进行分析 从不同的协议层出发 横向分析各个层次内的调用关系 从几种典型的协议模块出发 纵向分析各模块的跨层调用关系 整体调用关系 图给出了LwIP的整体调用关系 基本上涵盖了LwIP的主要功能模块和绝大部分的函数调用 图中只标注了对LwIP的整个软件体系起着重要支撑作用的主干函数 协议层内的调用 TCP IP协议栈是按功能层组织的 每一层都为上一层提供服务 并使用下一层提供的服务 在4层TCP IP模型中 从下至上依次是网络接口层 网际层 运输层和应用层 1 网络接口层网络接口层是较高协议与局域网接口的地方 当主机通过查询或者中断方式得知网络芯片接收到数据帧时 LwIP协议栈对该数据帧进行解码 并判断数据帧的协议类型 如果是IP协议 则将该帧传递给上层 网际层 的ip input 函数进行处理 如果是ARP协议 则直接传给本层的arp input 函数 该函数根据需要决定是否调用arp replay 进行ARP应答 当上层有数据需要通过网络接口层进行发送时 当前网络接口的输出函数netif output 将会被调用 以完成真正的数据发送过程 协议层内的调用 协议层内的调用 2 网际层网际层负责网间寻址 IP地址 数据封装 路由选择 错误处理和诊断等典型协议有IP协议和ICMP协议 当从下层 网络接口层 接收到IP数据报时 调用ip input 函数进行处理 根据IP数据报的协议字段 LwIP决定将该数据报传给上层 运输层 还是传给本层 如果IP净荷中承载的是ICMP协议 则本层的icmp input 函数将会调用 当不论是上层还是本层有数据需要从网际层发送出去时LwIP将会调用ip output 发送数据 或者先调用ip route 找到一个合适的网络接口再调用ip output if 发送数据 实际上ip output 也是通过先调用ip route 再调用ip output if 来实现的 协议层内的调用 协议层内的调用 3 运输层运输层负责在网际设备之间运输数据 以可靠或不可靠的方式进行 TCP和UDP 当下层 网际层 有数据传给运输层时LwIP会根据数据类型的不同 是TCP还是UDP 调用该层的tcp input 或者udp input 经过一定处理后 LwIP将数据由tcp receive 或udp input 提交给上层 应用层 一般会调用事先注册的接收函数 当上层需要发送数据时LwIP选择调用tcp write 或者udp send 对数据进行处理最后通过tcp output 或udp send 将数据交给下层 协议层内的调用 协议层内的调用 4 应用层用户的应用运行在应用层 该层使用户可以根据自己的需要对数据进行处理 用户需要发送数据时由LwIP根据数据类型 TCP或UDP 调用下层 运输层 对应的发送函数 应用层并不需要直接关注数据是怎样发送出去的 用户接收的数据一般由LwIP调用下层的接收函数送达 此后用户可以根据实际情况实现应用程序 典型模块的跨层调用 对于某一个协议来说它一般只隶属于某一个层次 ARP除外 但往往会有其它层次调用该协议的有关函数而该协议一般也会主动调用其它层次的有关函数 1 IP模块LwIP的较早期版本实现了IP层大部分的基本功能 能够发送 接收以及转发信息包 接收信息包由网络设备驱动调用ip input 函数开始处理 完成对IP版本字段及包头长度的初始完整性检查同时还要计算和验证包头校验和函数检查目的地址是否与网络接口的IP地址相符以确定信息包是否到达预定主机 如果一个到达的信息包被发现已经到达了目的主机 则由协议字段来决定信息包应该传送到哪一个上层协议 典型模块的跨层调用 外发的信息包由ip output 函数处理 该函数使用ip route 函数查找适当的网络接口来传送信息包 当外发的网络接口确定后 信息包传给以外发网络接口为参数的ip output if 函数 所有的IP包头字段被填充 并且计算IP包头校验和 IP信息包的源及目标地址作为参数被传递给ip output if 函数 传输层协议UDP与TCP在计算传输层校验和的时候需要拥有目标IP地址 因此一些传输层函数可能会直接直接调用ip route 函数确定接口 这样这些函数在外发数据前就没有必要再对网络接口链表进行检索 而是直接调用ip output if 函数外发数据 典型模块的跨层调用 如果没有网络接口的地址与到达的信息包的目标地址相同 信息包应该被转发 由ip forward 函数完成 TTL字段值被减少 当减为0的时候 将会给IP信息包的最初发送者发送ICMP错误信息 并抛弃该信息包 因为IP包头被改变 因此需要调整IP包头校验和 最后 信息包被转发到适当的网络接口 典型模块的跨层调用 2 ICMP模块ICMP信息包由ip input 函数收到后 转交给icmp input 函数对ICMP包头解码 然后进行适当的动作 如果需要对回送请求进行应答 则调用ip output 函数发送应答报文 某些ICMP消息被传递给上层协议 由传输层的特定函数处理 ICMP目标不可到达消息可以由传输层发送 特别是UDP如udp input 就可以调用icmp dest unreach 函数完成这项工作 icmp dest unreach 最后也会调用ip output 发送ICMP报文 典型模块的跨层调用 典型模块的跨层调用 3 UDP模块当一个UDP数据包到达时IP层调用udp input 函数将数据包移交给udp input 如果需要的话 LwIP会在这里对UDP校验和进行检查 为了找到匹配的UDPPCB LwIP会对UDPPCB全局链表进行线性搜索 如果当前链表中存在匹配的UDPPCB 则其recv函数会被调用 发送数据的过程由应用程序调用udp send 函数发起 为了计算校验和 该函数会调用ip route 确定网络接口 因为该接口地址将在校验和的计算过程中用到 最后 信息包被移交给ip output if 函数传送 典型模块的跨层调用 典型模块的跨层调用 4 TCP模块TCP处理比UDP处理要复杂得多与TCP输入相关的函数tcp input tcp process tcp receive 与TCP输出有关的函数tcp write tcp enqueue tcp output 典型模块的跨层调用 典型模块的跨层调用 TCP数据的发送过程一般是由应用层发起 应用层调用tcp write 而tcp write 再调用tcp enqueue tcp enqueue 函数会在必要时将数据分割成适当大小的TCP段 然后把这些TCP段放到所属连接的传输队列中 这时tcp output 函数会判断接收器窗口是否拥有足够大的空间 阻塞窗口是否也足够大 如果条件满足 就调用ip route 找到一个合适的接口 再调用ip output if 完成发送过程 即使当时不能发送也不要紧 这是因为LwIP设置了定时器函数tcp tmr 该函数每隔固定时间就会被调用一次 tcp tmr 会对当前连接的传输队列进行分析 并根据需要调用tcp output 执行数据发送操作 典型模块的跨层调用 TCP数据的接收过程由网络接口层发起 网络接口层将数据包传递给ip input 函数 该函数验证IP头后移交TCP段给tcp input 函数 tcp input 函数主要完成两项工作初始完整性检查 也就是校验和验证与TCP选项解析判定这个TCP段属于哪个TCP连接 接着 这个TCP段到达tcp process 函数 tcp process 函数实现了TCP状态机 任何必要的状态转换都在这里实现 当该TCP所属的连接正处于接受网络数据的状态时 tcp receive 函数将被调用 最后 tcp receive 函数将数据传给上层的应用程序 完成接收过程 如果收到一个ACK应答确认数据 表明接收器同意接收更多的数据 此时tcp output 函数将会被调用 LwIP的内存管理 LwIP的包缓冲区pbufpbuf是LwIP信息包的内部表示 pbuf结构既支持动态内存分配以保存信息包内容 又支持让信息包数据驻留在静态存储区 多个pbuf可以通过一个链表结构链接成一个pbuf链 从而使一个信息包穿越多个pbuf pbuf的内部结构定义为structpbuf structpbuf next 指向下一个pbufvoid payload 指向实际的数据负载u16 ttot len pbuf链的数据负载总长度u16 tlen 该pbuf的数据负载长度u16 tflags pbuf的类型标志u16 tref pbuf被引用的次数 LwIP的内存管理 pbuf结构包括两个指针 两个长度字段 一个标志字段和一个引用计数字段 next指针指向pbuf链中下一个pbuf的位置 payload指针指向pbuf中数据负载的开始位置len字段包含pbuf中数据内容的长度 tot len字段包含当前pbuf的长度与在这个pbuf链中随后的所有pbuf的len字段之和flags字段标识pbuf的类型 ref字段指出pbuf被引用的次数 pbuf有四种类型PBUF RAMPBUF ROMPBUF REFPBUF POOL PBUF RAM类型的pbuf PBUF RAM在事先划分好的内存堆栈中分配 用于存放应用程序动态产生的数据 图示的是一个PBUF RAM类型的pbuf实例 其实际的数据负载存放在由协议栈管理的存储区中 既然PBUF RAM类型的pbuf用于应用程序发送的数据被动态生成的情况 那么在这种情况下pbuf系统不仅为应用数据分配内存 还应给为这些数据预置的包头分配内存 pbuf系统不可能预先知道为这些数据预置什么样的包头 因而考虑最坏的情况 PBUF ROM PBUF REF类型的pbuf PBUF ROM类型的pbuf的payload指针指向不由协议栈管理的外部存储区如应用程序管理的存储器为用户数据分配的缓存 由于由应用程序交付的数据不能被改动因此就需要动态地分配一个PBUF RAM来装载协议的首部然后将PBUF RAM 首部 添加到PBUF ROM 数据 的前面 这样就构成了一个完整的数据分组 pbuf链 PBUF ROM PBUF REF类型的pbuf PBUF ROM PBUF REF类型的pbuf 图中的PBUF ROM还可以是PBUF REF 二者的特性非常相似 都可以实现数据的零拷贝 但是当发送数据需要排队时就表现出PBUF REF的特性了 例如待发送的分组需要在ARP队列中排队 假如这些分组中有PBUF ROM类型的pbuf 则直到分组被处理之前 被引用的应用程序的这块存储区域都不能另作它用 但如果是PBUF REF类型的pbuf LwIP则会在数据分组排队时为PBUF REF类型的pbuf分配缓存 PBUF POOL或PBUF RAM 并将引用的应用程序的数据拷贝到分配的缓存中 这样应用程序中被引用数据的存储区域就能被释放 PBUF POOL类型的pbuf PBUF POOL是具有固定容量的pbuf 其容量大小通过宏定义来指定 在协议栈管理的内存中初始化了一个pbuf池 具有相同尺寸的pbuf都是从这个pbuf池中分配得到 一般使用多个PBUF POOL链接成一个链表 用于存储数据分组 PBUF POOL类型的pbuf PBUF POOL类型的pbuf PBU POOL主要用于网络设备驱动层由于分配一个pbuf的操作可以快速完成 所以PBUF POOL非常适合用于中断处理 一般来说 收到的pbuf是PBUF POOL类型 发送出的pbuf是PBUF ROM或PBUF RAM类型 不同类型的pbuf拥有各自的特点和不同的使用目的 因此只有正确选用 才能最好地发挥LwIP的特性 LwIP的内存管理 LwIP的内存区域主要用于装载待接收和发送的网络数据分组 当接收到分组或者有分组要发送时 LwIP协议栈为这些分组分配缓存 在接收到的分组交付给应用程序或者分组己经发送完毕后 LwIP协议栈对分配的缓存进行回收利用 协议栈分配的缓存必须能容纳各种大小的报文例如从仅仅几个字节的ICMP应答报文到几百个字节的TCP分段报文 PBUF RAM的内存管理 LwIP协议栈首先从系统内存中开辟一块连续的静态存储区域该区域的大小可以事先通过宏定义指定 协议栈将该区域作为PBUF RAM的专用区域所有与PBUF RAM有关的内存操作都被限制在该区域内 从而确保了协议栈不会因非法访问系统内存的其它区域而扰乱其它程序的正常运行 PBUF RAM的内存管理 为了方便内存管理 协议栈定义了一个比较小的结构体mem 并将该结构体置于内存分配块的顶部来保存内存分配记录 该结构体拥有三个成员变量 分别为两个 指针 和一个标志 其中next与prev分别指向内存的下一个和上一个分配块 used标志标示该内存块是否已被分配 next和prev并不是真正的指针 它们本质上是数组的下标 并没有直接指向真正的地址 PBUF RAM的内存管理 1 初始化使用PBUF RAM内存之前 需对PBUF RAM的专用内存区域进行初始化工作 即对该区域进行一定的格式设置 LwIP协议栈在该区域的头部和尾部各设置了一个mem结构以协助管理内存 如图所示 在头部的mem结构中 next指向尾部mem prev指向该区域的起始处 used 0 表明该mem结构后面的区域尚未使用 在尾部的mem结构中 next和prev均指向该mem自身 used 1 表明该mem结构后面无可用的内存 在该区域的末尾处 协议栈预留了对齐空间 其主要目的是防止操作过程中因对齐而导致的对该专用区域之外的存储空间的越界访问 PBUF RAM的内存管理 PBUF RAM的内存管理 2 分配PBUF RAM内存块分配内存时首先根据所申请分配的大小来搜索所有未被使用的内存分配块搜索到的最先满足条件的内存块将分配给申请者 第一次分配时只要申请分配的大小没有超出限制 便会在PBUF RAM专用存储区域的开头分配所需要的内存 PBUF RAM的内存管理 经过多次的内存分配和释放操作后 PBUF RAM存储区中会存在多个大小不一的未使用块 此时如果需要分配一个新的内存块 有可能搜索到的第一个空闲内存块空间不够 在这种情况下 内存管理机制会继续往后搜索未使用块 直到搜索到足够大的空闲内存块或搜索到存储区的末尾 PBUF RAM的内存管理 3 释放PBUF RAM内存块对不再利用的内存块 需要进行回收 以便下次需要分配内存块时重新使用 回收内存块时 管理该内存块的mem结构的used标志将被清零 以表明该内存块已不再被使用 可以重新对其进行分配 PBUF RAM的内存管理 为了防止内存碎片的产生 每回收一个内存块后 其上一个与下一个分配块的used标志将会被检查 如果它们中的任何一个还未被使用 used 0 则这个内存块将被合并到一个更大的未使用内存块中 只有经过了以上操作后 释放内存的工作才算是已经完成 PBUF RAM的内存管理 合并相邻空闲块的一个示例 PBUF RAM的内存管理 4 调整PBUF RAM内存块大小在内存的使用过程中 有时希望调整已分配的内存块的大小 根据调整的方向 减小 增大 不同 处理的机制也不一样 调整前先对腾出的内存块大小进行预算 如果该值小于mem结构的长度加内存块的最小长度 即无法另行分配一个最小长度的内存块 则调整不被执行 调整时将在腾出的内存块开头置以一个新的mem结构以对其进行管理 该mem结构的used标志为0 表示可以对其进行分配使用 同释放内存时一样 与新的内存块相邻的内存块的used标志同样会被检查 以防止碎片产生 PBUF RAM的内存管理 给出了减小既定内存块大小的示意图 PBUF RAM的内存管理 增大既定内存块的大小时所采取的机制与上述操作大为不同 如果希望将某内存块的大小调整为newsize 则处理过程是先分配一块大小为newsize的空闲内存块 然后将原内存块的内容复制到新内存块中 最后再释放原内存块 PBUF ROM PBUR REF的内存管理 对于PBUF ROM PBUF REF LwIP协议栈同样为其开辟了一块连续的存储区域 协议栈定义了结构体memp以协助PBUF ROM PBUF RAM的内存管理 该结构体只有一个成员next 为指向下一个相同结构的存储区的指针 PBUF ROM PBUF REF类型存储区域初始化后的结构示意图 多个pbuf在内存中以链表的形式存在 该种类型pbuf的操作方法可以参考链表的一般操作方法 PBUF POOL的内存管理 PBUF POOL类型的pbuf同样拥有自己专用的存储区域 该区域通过预先从系统内存中分配而得 区域大小由PBUF POOL类型的pbuf个数和每个pbuf的缓冲区大小等参数共同决定 这些参数都可以事先通过宏定义指定 对PBUF POOL类型的内存管理 LwIP协议栈并没有额外引入类似PBUF RAM的mem结构或是PBUF ROM的memp结构 而是直接采用pbuf结构对其进行管理 PBUF POOL的内存管理 PBUF POOL存储区域的结构如图所示 对该类型pbuf的操作与普通链表的操作并无不同 每个PBUF POOL的数据缓冲区都紧跟在pbuf结构后面 并且大小相同 这点与PBUF ROM不同 因为PBUF ROM的数据缓冲区不在LwIP协议栈管理的区域 并且大小不尽相同 LwIP移植 无RTOS时的移植LwIP既可以在无RTOS RealTimeOperatingSystem 实时操作系统 的环境下运行 也可以很方便地移植到RTOS之上 移植过程中对于LwIP核心模块没必要也不建议进行修改 而真正的工作是结合实际的软硬件环境 针对与移植密切相关的相关文件与相关函数进行定制 LwIP移植 移植函数为了将LwIP移植到特定的开发平台上 需要完成与网络接口有关的底层函数 这些底层函数集中在 src netif ethernetif c文件中 建议将 ethernet 替换成能更好地描述所选网络接口的词汇 如华中科技大学瑞萨高级嵌入式控制器实验室自行开发的RenesasM16C 62P嵌入式开发平台采用的网络芯片是CS8900A 该文件便用cs8900if c文件进行了替代 文件中凡是用ethernet命名的函数 也一律用cs8900进行了替代 LwIP提供的ethernetif c文件给出了网络接口驱动的整体框架 用户需要自己完成的函数主要有3个 分别是底层初始化函数low level init 底层输入函数low level input 底层输出函数low level output 无RTOS时的移植 1 底层初始化函数low level init 原型为staticvoidlow level init structnetif netif 该函数用来对网络接口进行初始化 任何与初始化网络接口有关的操作都可以在该函数内实现 如对网络接口有关参数进行配置 或是完成网络芯片硬件上所需的初始化操作等 2 底层输入函数low level input 函数原型为staticstructpbuf low level input structnetif netif 该函数为到达的数据包分配pbuf 通常是一个pbuf链 并将数据包从网络接口传入至pbuf链中 数据具体接收过程的实现与网络接口硬件有关 将数据装载至pbbuf时 需对pbuf结构的各字段进行正确填充 使其形成逻辑上的pbuf链 无RTOS时的移植 通常 为收到的数据分配的pbuf是PBUF POOL类型 因为分配一个PBUF POOL可以很快完成 3 底层输出函数low level output 函数原型为 staticerr tlow level output structnetif netif structpbuf p 该函数实现真正的的数据包发送过程当需要发送数据包时 数据包装载在事先已分配好的pbuf 链 中 LwIP将pbuf作为参数传入给该函数 由该函数负责将数据包发送至指定的网络接口中 数据具体发送过程的实现同样与网络接口硬件有关 无RTOS时的移植 几个定时器函数在LwIP的移植过程中 注意有几个定时器函数必须每隔固定时间就调用一次 具体采用什么机制实现这一操作并无限制etharp tmr tcp fasttmr tcp slowtmr 这些函数的运行间隔周期可以通过宏定义指定 各函数的具体定义可在LwIP提供的源码中找到 LwIP在uC OS II下的移植 为了方便LwIP在RTOS下的移植 属于操作系统的函数调用及数据结构并没有在代码中直接使用 而是用操作系统模拟层来代替对这些函数的使用 操作系统模拟层使用统一的接口提供定时器 进程同步及消息传递机制等诸如此类的系统服务原则上 移植LwIP只需针对目标操作系统修改模拟层实现即可 LwIP在uC OS II下的移植 模拟层主要实现以下4大功能 定时与超时处理LwIP可以为某一线程注册若干个超时处理函数 当超时时限溢出时便会调用一个已注册的函数 进程同步进程同步机制为多个进程之间的同步操作提供支持 一般可以用信号量来实现 如果选用的RTOS不支持信号量 则可以使如条件变量等其它基本的同步方式来模拟 消息传递消息传递机制可通过一种称作邮箱的抽象方法来实现 邮箱有两种基本操作 向邮箱投递 post 一则消息和从邮箱中提取 fetch 一则消息线程管理对LwIP协议栈的线程进行管理和维护 主要指创建线程 移植相关文件与函数 移植过程中需要创建或修改的源文件和头文件位于目录src arch之下目录组织结构如图所示 移植相关文件与函数 头文件主要是一些宏定义 包括数据类型的定义和有关结构的封装等 而主要的功能函数均在sys arch c源文件中实现 此外 sys c和sys h两个文件虽无需作任何修改 但与以上文件 尤其是sys arch c 关联紧密 有助于更好地理解LwIP在RTOS下的移植实现 LwIP在设计时就考虑到了将来的RTOS移植问题 为了适应不同的操作系统 LwIP并没有在代码中使用针对某个特定RTOS的系统调用和数据结构 而是提供操作系统模拟层作为LwIP和RTOS的一个接口 为了理解LwIP在RTOS下的移植实现过程 选用嵌入式实时操作系统uC OS II为例 对LwIP在uC OS II下的移植进行说明 uC OS II是专为嵌入式应用设计的实时内核 关于其详细信息可参考其官网 移植相关文件与函数 根据LwIP源码提供的sys arch txt文件 需要实现的函数有 voidsys init void 被调用来初始化操作系统模拟层 sys sem tsys sem new u8 tcount 创建并返回一个新的信号量 参数count指定信号量的初始状态 voidsys sem free sys sem tsem 删除一个信号量 voidsys sem signal sys sem tsem 发出一个信号量 u32 tsys arch sem wait sys sem tsem u32 ttimeout 等待一个信号量 该操作会阻塞调用该函数的线程 sys mbox tsys mbox new void 创建一个空的邮箱 voidsys mbox free sys mbox tmbox 删除一个邮箱 移植相关文件与函数 voidsys mbox post sys mbox tmbox void msg 向指定的邮箱发送一则消息 u32 tsys arch mbox fetch sys mbox tmbox void msg u32 ttimeout 从邮箱中提取一则消息 该操作同样会阻塞调用该函数的线程 structsys timeouts sys arch timeouts void 返回指向当前线程的sys timeouts结构的指针 LwIP的每个线程都有自己的超时等待属性 每个线程都分配了一个超时等待的数据结构sys timeout 并把这个数据结构存放于链表sys timeouts中 该函数的作用是通过查询来获得一个指向当前线程使用的sys timeouts结构的指针 sys thread tsys thread new void thread void arg void arg intprio 创建一个新的LwIP线程 超时处理的实现 如前所述 LwIP的每个线程都有自己的超时等待属性 为了顺利理解LwIP的这一机制 先引入与之相关的几种数据结构 sys timeout是线程的超时等待数据结构 其内部结构定义如下 structsys timeout structsys timeout next 指向链表中的下一个sys timeoutu32 ttime 超时时限 ms sys timeout handlerh 超时处理函数void arg 超时处理函数的参数 其中sys timeout handler是指向超时处理函数的指针 其定义为typedefvoid sys timeout handler void arg 多个sys timeout可以链接成一个链表 如图所示 超时处理的实现 sys timeouts是sys timeout链表的表头 它只包含一个元素 即指向sys timeout结构的指针 其定义如下 structsys timeouts structsys timeout next 指向链表第一个sys timeout 加上sys timeouts结构后 LwIP线程的超时等待链表结构如图所示 超时处理的实现 超时处理的实现 timeoutlist将一个sys timeouts结构和优先级联系在一起 这样便于根据当前优先级查找对应的sys timeouts链表 其定义如下 structtimeoutlist structsys timeoutstimeouts 超时等待链表INT8Uprio 优先级 每个线程都有一个对以的timeoutlist结构 通过该结构的timeouts元素可以定位超时等待列表的表头 从而确定该线程的所有超时处理函数 超时处理的实现 LwIP在RTOS上的移植过程中需要实现的与超时处理有关的函数是sys arch timeouts 1 移植函数sys arch timeouts sys arch timeouts 函数的作用是通过查询机制 获取指向当前线程的sys timeouts结构的指针 相当于定位超时等待链表的表头 函数的原型如下 structsys timeouts sys arch timeouts void 该函数表面上没有参数 但实际上调用该函数的线程有自己的优先级 因此可以利用当前线程的优先级充当函数的隐含参数 这样处理带来的限制是一个线程不能通过调用该函数来获取另一个线程的sys timeouts结构 但这一般不会引起什么问题 超时处理的实现 sys timeouts结构和当前线程的优先级一起封装在timeoutlist结构中 为了存储线程的timeoutlist结构 在sys arch c文件中定义了一个timeoutlist数组 staticstructtimeoutlisttimeoutlist LWIP MAX TASKS LWIP MAX TASKS是最大的LwIP线程数 可以事先进行配置 每次调用sys thread new 创建一个新的线程时 都会依序取出一个数组元素 用当前线程的优先级对数组元素的prio字段进行填充 sys arch timeouts 函数通过线性搜索的方法对数组元素进行遍历 直到发现某个数组元素的prio字段与当前优先级相同为止 而该数组元素的timeouts字段正是我们需要的目标 超时处理的实现 超时处理的实现 2 相关函数sys timeout sys timeout 函数用以向当前线程增加一个超时处理函数 其原型如下 voidsys timeout u32 tmsecs sys timeout handlerh void arg sys timeouts 函数首先从内存中申请一块空间 以存放一个sys timeout结构 如申请成功则利用函数的实参对结构的各字段进行填充 要向当前线程注册一个超时处理函数 sys timeouts 会通过sys arch timeouts 函数获取当前线程的sys timeouts结构 超时处理的实现 第一次调用sys timeout 注册一个超时处理函数时 直接将sys timeout结构链接在当前线程的sys timeouts结构即可 如图所示 超时处理的实现 应用程序可能会多次注册超时处理函数或删除超时处理函数 这样处理后一个线程的sys timeouts链表中可能会同时存在多个sys timeout结构 在这种情况下 向线程添加一个超时处理函数略微复杂 因为sys timeout结构必须插入到链表的恰当位置 实际上如果一个线程有多个超时处理函数 LwIP会按照链表的逻辑顺序依次结算 这里所谓恰当的位置 就是比较当前链表节点的超时时限和待插入节点的超时时限 保证插入该节点后不会影响原有任一节点的超时等待属性 超时处理的实现 如图所示 假设当前线程已注册3个超时处理函数 对应有3个sys timeout结构 其超时时限分别是time1 100 time2 40 time3 80 现要注册一个超时时限为time4 160的超时处理函数 为了确定恰当的插入位置 可以沿着链表逐次推算超时时限 分析过程如下 1time1time4 next4在next3之前经以上步骤 next4的位置已经确定 即位于next2和next3之间 注意插入next4节点后 next4节点及紧挨在next4后面的next3节点的time属性值需做对应调整 调整后的结果如图所示 超时处理的实现 超时处理的实现 如果新节点next4的超时时限time4取其它值 则可能会出现一些特殊情况 time4 time1 即新节点的time值比第一个节点还小 这种情况直接将next4节点插入到next1节点之前 并调整time1 time1 time4即可 超时处理的实现 time4 time1 time2 即新节点的time值等于前面若干节点他time之和 这种情况next4将插入到next2的后面 next4节点的time值调整为0 而与next4相邻的next2节点和next3节点则无需调整time属性 线程在依序处理超时等待函数过程中 一旦执行完next2节点的超时处理函数h2 arg2 会立即执行next4节点的超时处理函数h4 arg4 time4 time1 time2 time3 即新节点的time值大于现有所有节点time之和 这种情况next4节点将插入到最后 并调整time4 time4 time1 time2 time3 next4 NULL 超时处理的实现 3 相关函数sys untimeout sys untimeout 函数与sys timeout 函数的作用恰好相反 用以删除当前线程某一指定的超时处理函数 函数原型如下 voidsys untimeout sys timeout handlerh void arg 与sys timeout 相比 sys untimeout 函数同样会调用sys arch timeouts 获取当前线程的sys timeouts链表结构 函数通过一种简单的线性搜索的方法 从表头开始遍历 直到找到一个超时处理函数h和参数arg均符合的sys timeout结构 将该结构所在的节点从链表中删除 并调整紧挨其后的节点 如果有的话 的超时时限属性 最后释放该结构占用的内存 超时处理的实现 假设某一线程的超时处理函数链表如图所示 超时处理的实现 如果线程希望删除h2处理函数 即执行sys untimeout h2 arg2 next2节点将从原链表中删除 同时next3节点的time3将会调整为time3 time3 time2 调整后的状态为 超时处理的实现 但如果线程不是要删除h2处理函数 而是要删除最后一个超时处理函数 即执行sys untimeout h3 arg3 这种情况next3节点从链表中删除后 没有后续节点需要调整超时时限属性 结果如下 超时处理的实现 4 超时处理函数的使用在等待信号量或等待消息的过程中 LwIP会对超时等待链表中的超时处理函数进行处理 对一个线程来讲 要么通过调用sys sem wait 等待一个信号量 要么通过调用sys mbox fetch 等待一则消息 至少要采取一种方法阻塞当前线程 否则注册的超时处理函数将无法正常执行 鉴于邮箱结构比信号量结构占用更多的资源 因此通常通过永久等待一个信号量来实现线程阻塞 如需每隔一定周期就执行一次某函数 则必须在超时处理函数中重新注册自己 例如需要每隔250ms就执行一次tcp tmr 通常可以采用下面的方式实现 向当前线程注册一个超时处理函数sys timeout u32 t OS TICKS PER SEC 4 sys timeout handler TCP Timer NULL 如需周期执行tcp tmr 则需在TCP Timer 中重新注册自己voidTCP Timer void p arg tcp tmr 每隔250ms执行一次sys timeout u32 t OS TICKS PER SEC 4 sys timeout handler TCP Timer NULL 进程同步的实现 进程同步机制是任务之间通信的一种重要方式 通常可以由信号量实现 uC OS II对信号量有较全面的支持 因此移植过程中比较方便实现 uC OS II实现了信号量和互斥型信号量 这里采用uC OS II的信号量实现 由于uC OS II支持信号量的各种操作 并且可以满足LwIP对信号量的要求 因此只需对相关结构和函数进行重新封装即可 进程同步的实现 函数sys sem wait 该函数是由LwIP应用程序调用的用来等待一个信号量的函数 但它会在等待信号量的过程中对当前线程的超时等待函数进行处理 函数原型如下 voidsys sem wait sys sem tsem 该函数首先调用sys arch timeouts 获取当前线程的超时等待函数链表 以对超时等待链表中的超时处理函数依次结算 真正实现等待一个信号量的过程由sys arch sem wait 完成 现仍以下面的超时等待链表为例 分析sys sem wait 在等待信号量过程中对超时处理函数的处理 进程同步的实现 1执行sys arch sem wait sem time1 如超时溢出则表明在time1时间内一直未成功等到信号量 此时执行超时处理函数h1 arg1 同时将next1节点从链表中删除 并转到步骤2 如在time1时间内成功等到信号量 则不论实际消耗的等待时间是多少 LwIP一律认为消耗时间为1ms 并调整time1 time1 1 此时转到步骤4 2执行sys arch sem wait sem time2 只有在time1时限耗尽的情况下 才会执行sys arch sem wait sem time2 与1类似 如未等到信号量则执行h2 arg2 并转到步骤3 否则调整time2 time2 1并转到步骤4 进程同步的实现 3调用sys arch sem wait sem time3 只有在next3节点前面的所有节点的超时时限均已耗尽的情况下 才会执行sys arch sem wait sem time3 由于next3已经是最后一个节点 因此不论成功等到信号量与否 均会转到步骤4 4sys sem wait 函数返回或作永久等待 如果成功等到了信号量 sys sem wait 函数返回 但如果超时等待链表中所有超时时限均已耗尽且所有超时处理函数均已执行后 仍未等到信号量 则sys sem wait 会调用sys arch sem wait 0 一直等到信号量有效为止 进程同步的实现 一种特殊情况是当前线程超时等待链表为空 也就是没有超时等待函数 sys sem wait 会直接调用sys arch sem wait 0 做永久等待 另一种情况是在等待信号量的过程中发现某一节点的超时时限time 0 表明该节点的超时时限已经耗尽 需立即执行节点对应的超时处理函数h arg 注意time 0与sys timeouts链表为空是截然不同的 time 0只是表明该节点的超时时限已耗尽sys timeouts为空则意味着当前线程没有任何函数需要做超时等待 消息传递的实现 消息传递是任务之间通信的另一种重要方式 通常使用一种称为邮箱的抽象方法来实现 邮箱有两种基本的操作 邮递 post 发送一则消息 操作不会阻塞进程提取 fetch 等待一则消息 操作可能会阻塞进程 uC OS II提供了消息邮箱和消息队列两种机制 区别是消息邮箱一次只能处理一则消息 而消息队列可以存储多则消息 为了使LwIP更好地运作 采用uC OS II的消息队列实现LwIP所需的消息传递机制 线程管理的实现 在线程管理方面 LwIP只提供了创建线程的操作 由于uC OS II没有采用 线程 这一概念 而是采用 任务 的概念 LwIP的线程管理实际上是通过uC OS II的任务管理机制实现的 每个线程都有自己的超时等待属性 为了区别不同线程的超时等待属性 在创建线程的过程中会将优先级prio填入到一个timeoutlist结构的prio成员中 如图所示 线程管理的实现 由于uC OS II中每个任务都具有唯一的优先级 因此prio可以作为LwIP线程的一个标识 以区分不同的线程 实际上sys arch timeouts 正是通过这一标识来定位当前线程的超时等待链表的 LwIP网络编程应用实例 为了对LwIP的移植和应用进行测试和验证 以一个具体的应用实例来说明LwIP网络编程的一般方法 目的是设计和实现一个简单的嵌入式WEB服务器 该WEB服务器可以响应来自浏览器的HTTPGET请求 并在发送请求的浏览器上显示一个小型页面 实验平台准备 硬件平台的一个最基本要求是提供对以太网接口的支持 选用的是华中科技大学瑞萨高级嵌入式控制器实验室自主研发制作的RenesasM16C 62P嵌入式开发平台 该平台采用瑞萨科技 RENESAS 的M16C 62P单片机作为主控制器 通过集成一块CS8900A网络芯片来实现网络数据收发功能 实验平台准备 软件平台最重要的部分是开发环境 采用Renesas的High performanceEmbeddedWorkshop进行编程开发 为了方便调试 华中科技大学瑞萨高级嵌入式控制器实验室开发了专门针对R

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论