Contiki协议栈Rime:缓冲区管理packetbuf management

2024-04-08 04:32

本文主要是介绍Contiki协议栈Rime:缓冲区管理packetbuf management,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

更多的Contiki协议栈知识,请参考索引目录:
《Contiki协议栈:索引目录》

1 概述

  关于Rime的缓冲区管理这一块,能在网上搜到很多博客,但是我想说的是,99%+都是过时的,坑爹啊!Contiki的开发非常活跃,所以对代码的改进很多,而Rime的缓冲区管理这也在今年二月份进行了优化,由之前难以理解的、晦涩的“双头栈”改为了现在通俗易懂的结构。双头栈有多晦涩,你将contiki的代码reset到今年二月份之前去看看就知道了。
  不过不要马虎,虽然现在的缓冲管理变简单了,但是它很重要!很重要!!很重要!!!
  为啥说它很重要呢?

  • 其一,Rime整个协议栈里面的子协议,都得使用它。(至于uIP协议会不会使用它,我还没接触过,不知道呢)
  • 其二,底层协议,比如MAC,RDC,FRAMER,LLSEC都得使用它。
  • 其三,甚至某些应用程序也会用到它。还记得博客《Contiki协议栈Rime:引子》中的匿名广播的例程吗?里面有packetbuf_copyfrom("Hello", 6);这句话,它就使用到了packetbuf。

缓冲区的作用很简单,将发出和收到的数据包(包括数据和包属性)都存储在一个单一的缓冲区packetbuf,由头部和数据两部分组成。
相关代码位于contiki/core/net/packetbuf.[ch]

2 相关变量

packetbuf

packbuf就是Rime的缓冲区,它是一个指向数组的指针,其定义为:

/* 以下声明确保包缓冲区可以对齐32bit边界,在一些平台(最常见的如msp430或OpenRISC),在访问字节时,有可能会出现非对齐包缓冲而导致问题的发生。 */
static uint32_t packetbuf_aligned[(PACKETBUF_SIZE + 3) / 4];
static uint8_t *packetbuf = (uint8_t *)packetbuf_aligned;

其中,PACKETBUF_SIZE被定义为:

#ifdef PACKETBUF_CONF_SIZE
#define PACKETBUF_SIZE PACKETBUF_CONF_SIZE
#else
#define PACKETBUF_SIZE 128 // 缓冲区默认长度为128个字节
#endif

所以默认情况下,Rime的buffer是大小为128字节的连续的内存空间。

需要说明的是,这里的PACKETBUF_SIZE是指整个缓冲的长度,包括头部和数据。在以前的机制中,这个PACKETBUF_SIZE只包括数据部分,头部还另外占据PACKETBUF_HDR_SIZE个字节的缓冲。

buflen hdrlen bufptr

这三个变量的定义如下:

static uint16_t buflen, bufptr;
static uint8_t hdrlen;

其中,
buflen:已使用的数据部分的长度
hdrlen:已使用的头部部分的长度
bufptr:这不是指针,而是一个整型变量。它相当于一个索引,指向缓冲的某个地址。这个变量在今后解析buffer时非常有用。

3 相关函数

packetbuf_clear

void packetbuf_clear(void)
{buflen = bufptr = 0;hdrlen = 0;packetbuf_attr_clear();
}

该函数负责清空数据,包括packetbuf指向的buffer,以及两个属性数组里的内容。在将包压入包缓冲之前,会调用该函数。

packetbuf_copyfrom

int packetbuf_copyfrom(const void *from, uint16_t len)
{uint16_t l;packetbuf_clear(); // 先清空属性数组和packetbufl = MIN(PACKETBUF_SIZE, len);  // 如果len大于PACKETBUF_SIZE,则截断memcpy(packetbuf, from, l);buflen = l;return l;
}

很容易理解:先清空属性数组和packetbuf,然后从from中拷贝len个长度的数据到packetbuf中。如果需要拷贝的长度但对于定义的buffer长度,则进行截断处理,只拷贝PACKETBUF_SIZE个字节。该函数返回所拷贝的数据的长度。

packetbuf_compact

void packetbuf_compact(void)
{int16_t i;if(bufptr) {/* 将数据部分向左移至头部后面 */for(i = 0; i < buflen; i++) {packetbuf[hdrlen + i] = packetbuf[packetbuf_hdrlen() + i];}bufptr = 0;}
}

该函数通过拷贝packetbuf的数据部分,使其紧紧跟随头部。头部和数据之间可能有若干个字节是隔开的,但是为啥会隔开呢?看后面的函数packetbuf_hdrreduce()和函数packetbuf_dataptr()就会明白。Rime中的协议在将包发送给设备驱动之前,会调用该函数,以确保包在内存中是连续的。
为了更容易理解,直接上图:

这里写图片描述

packetbuf_copyto

int packetbuf_copyto(void *to)
{if(hdrlen + buflen > PACKETBUF_SIZE) { // 怎么会发生这样的情况?return 0;}// 由于数据部分和头部中间可能有间隔,所以分开拷贝这两个部分,// 否则当中间真的存在间隔时,就会拷贝错误memcpy(to, packetbuf_hdrptr(), hdrlen);memcpy((uint8_t *)to + hdrlen, packetbuf_dataptr(), buflen);return hdrlen + buflen;
}

拷贝一个完整的packbuf到一个外部buffer。

packetbuf_hdralloc

int
packetbuf_hdralloc(int size)
{int16_t i;if(size + packetbuf_totlen() > PACKETBUF_SIZE) {return 0;}/* shift data to the right */for(i = packetbuf_totlen() - 1; i >= 0; i--) {packetbuf[i + size] = packetbuf[i];}hdrlen += size;return 1;
}

分配size个字节的头部空间。我们后面会看到,当需要向packetbuf中写入新的包属性时,会先调用此函数预分配空间。上图:

这里写图片描述

packetbuf_hdrreduce

int packetbuf_hdrreduce(int size)
{if(buflen < size) {return 0;}bufptr += size;buflen -= size;return 1;
}

该函数主要用于解析packetbuf。调用函数packetbuf_dataptr()会返回一个指向数据部分的第一个字节的指针。如果先调用packetbuf_hdrreduce(size1),此时bufptr会增加size个字节,如果再调用函数packetbuf_dataptr(),此时返回的函数指针就比之前的函数指针后移了size个字节。解析packetbuf的流程就是不断地调用packetbuf_hdrreduce()和packetbuf_dataptr()。

packetbuf_set_datalen

void
packetbuf_set_datalen(uint16_t len)
{PRINTF("packetbuf_set_len: len %d\n", len);buflen = len;
}

该函数用于设置数据部分的长度

packetbuf_dataptr

void *
packetbuf_dataptr(void)
{return packetbuf + packetbuf_hdrlen();
}

该函数返回指向数据部分第一个字节的指针。

packetbuf_hdrptr

void *
packetbuf_hdrptr(void)
{return packetbuf;
}

返回头部指针,即这个buf的指针

packetbuf_datalen

uint8_t
packetbuf_hdrlen(void)
{return bufptr + hdrlen;
}

返回数据部分长度

packetbuf_totlen

uint16_t
packetbuf_totlen(void)
{return packetbuf_hdrlen() + packetbuf_datalen();
}

返回头部和数据部分总长度

packetbuf_holds_broadcast

int
packetbuf_holds_broadcast(void)
{return linkaddr_cmp(&packetbuf_addrs[PACKETBUF_ADDR_RECEIVER - PACKETBUF_ADDR_FIRST].addr, &linkaddr_null);
}

通过比较属性数组中的PACKETBUF_ADDR_RECEIVER属性的值与linkaddr_null的值是否相等,判断packetbuf中是否包含广播地址。

关于这个函数的使用,在底层协议里有,暂时先不管,今后再补充

4 小结

在这篇博客中,简单介绍了缓冲区管理的各个函数,通过两张图片,应该更容易理解。但是函数太多了,我不可能相关的函数都画图,太浪费时间了。第一次阅读的话可能会觉得有些函数模棱两可,但是结合后面几篇博客,就能真正知道其具体应用了。

这篇关于Contiki协议栈Rime:缓冲区管理packetbuf management的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/884572

相关文章

用Microsoft.Extensions.Hosting 管理WPF项目.

首先引入必要的包: <ItemGroup><PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" /><PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" /><PackageReference Include="Serilog

关于如何更好管理好数据库的一点思考

本文尝试从数据库设计理论、ER图简介、性能优化、避免过度设计及权限管理方面进行思考阐述。 一、数据库范式 以下通过详细的示例说明数据库范式的概念,将逐步规范化一个例子,逐级说明每个范式的要求和变换过程。 示例:学生课程登记系统 初始表格如下: 学生ID学生姓名课程ID课程名称教师教师办公室1张三101数学王老师101室2李四102英语李老师102室3王五101数学王老师101室4赵六103物理陈

springboot家政服务管理平台 LW +PPT+源码+讲解

3系统的可行性研究及需求分析 3.1可行性研究 3.1.1技术可行性分析 经过大学四年的学习,已经掌握了JAVA、Mysql数据库等方面的编程技巧和方法,对于这些技术该有的软硬件配置也是齐全的,能够满足开发的需要。 本家政服务管理平台采用的是Mysql作为数据库,可以绝对地保证用户数据的安全;可以与Mysql数据库进行无缝连接。 所以,家政服务管理平台在技术上是可以实施的。 3.1

探索蓝牙协议的奥秘:用ESP32实现高质量蓝牙音频传输

蓝牙(Bluetooth)是一种短距离无线通信技术,广泛应用于各种电子设备之间的数据传输。自1994年由爱立信公司首次提出以来,蓝牙技术已经经历了多个版本的更新和改进。本文将详细介绍蓝牙协议,并通过一个具体的项目——使用ESP32实现蓝牙音频传输,来展示蓝牙协议的实际应用及其优点。 蓝牙协议概述 蓝牙协议栈 蓝牙协议栈是蓝牙技术的核心,定义了蓝牙设备之间如何进行通信。蓝牙协议

vue3项目将所有访问后端springboot的接口统一管理带跨域

vue3项目将所有访问后端springboot的接口统一管理带跨域 一、前言1.安装Axios2.创建Axios实例3.创建API服务文件4.在组件中使用API服务 二、跨域三、总结 一、前言 在Vue 3项目中,统一管理所有访问后端Spring Boot接口的最佳实践是创建一个专门的API服务层。这可以让你的代码更加模块化、可维护和集中管理。你可以使用Axios库作为HTT

【杂记-浅谈DHCP动态主机配置协议】

DHCP动态主机配置协议 一、DHCP概述1、定义2、作用3、报文类型 二、DHCP的工作原理三、DHCP服务器的配置和管理 一、DHCP概述 1、定义 DHCP,Dynamic Host Configuration Protocol,动态主机配置协议,是一种网络协议,主要用于在IP网络中自动分配和管理IP地址以及其他网络配置参数。 2、作用 DHCP允许计算机和其他设备通

逆向学习汇编篇:内存管理与寻址方式

本节课在线学习视频(网盘地址,保存后即可免费观看): ​​https://pan.quark.cn/s/3ceeb9ae6d98​​ 在汇编语言的世界中,内存管理和寻址方式是构建程序的基础。理解这些概念不仅对于编写高效的汇编代码至关重要,也是进行逆向工程分析的关键技能。本文将深入探讨内存管理的基本原则和多种寻址方式,并通过代码案例来展示它们的实际应用。 1. 内存管理 内存管理涉及如何分配

Git代码管理的常用操作

在VS022中,Git的管理要先建立本地或远程仓库,然后commit到本地,最后push到远程代码库。 或者不建立本地的情况,直接拉取已有的远程代码。 Git是一个分布式版本控制系统,用于跟踪和管理文件的变化。它可以记录文件的修改历史,并且可以轻松地回滚到任何历史版本。 Git的基本概念包括: 仓库(Repository):Git使用仓库来存储文件的版本历史。一个仓库可以包含多个文件

Yarn:引领JavaScript包管理新潮流

在浩瀚的JavaScript世界中,包管理工具如同一位精明的管家,帮助开发者管理着各式各样的代码包。而Yarn,这位新晋管家,以其高效、稳定和安全的特性,正逐渐成为开发者心中的新宠。本文将带您走进Yarn的世界,让您轻松掌握Yarn的强大特性和使用方法。 特性一:快速如闪电         想象一下,你是一位忙碌的图书馆管理员,每天需要整理成千上万的书籍。如果每本书的摆放都

【Qt6.3 基础教程 17】 Qt布局管理详解:创建直观和响应式UI界面

文章目录 前言布局管理的基础为什么需要布局管理器? 盒布局:水平和垂直排列小部件示例:创建水平盒布局 栅格布局:在网格中对齐小部件示例:创建栅格布局 表单布局:为表单创建标签和字段示例:创建表单布局 调整空间和伸缩性示例:增加弹性空间 总结 前言 当您开始使用Qt设计用户界面(UI)时,理解布局管理是至关重要的。布局管理不仅关系到UI的外观,更直接影响用户交互的体验。本篇博