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

相关文章

Python循环缓冲区的应用详解

《Python循环缓冲区的应用详解》循环缓冲区是一个线性缓冲区,逻辑上被视为一个循环的结构,本文主要为大家介绍了Python中循环缓冲区的相关应用,有兴趣的小伙伴可以了解一下... 目录什么是循环缓冲区循环缓冲区的结构python中的循环缓冲区实现运行循环缓冲区循环缓冲区的优势应用案例Python中的实现库

Linux中的缓冲区和文件系统详解

《Linux中的缓冲区和文件系统详解》:本文主要介绍Linux中的缓冲区和文件系统方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、FILE结构1、fd2、缓冲区二、文件系统1、固态硬盘2、逻辑地址LBA(一)数据块 Data blocks(二)inode表

nvm如何切换与管理node版本

《nvm如何切换与管理node版本》:本文主要介绍nvm如何切换与管理node版本问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录nvm切换与管理node版本nvm安装nvm常用命令总结nvm切换与管理node版本nvm适用于多项目同时开发,然后项目适配no

Redis实现RBAC权限管理

《Redis实现RBAC权限管理》本文主要介绍了Redis实现RBAC权限管理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1. 什么是 RBAC?2. 为什么使用 Redis 实现 RBAC?3. 设计 RBAC 数据结构

mac安装nvm(node.js)多版本管理实践步骤

《mac安装nvm(node.js)多版本管理实践步骤》:本文主要介绍mac安装nvm(node.js)多版本管理的相关资料,NVM是一个用于管理多个Node.js版本的命令行工具,它允许开发者在... 目录NVM功能简介MAC安装实践一、下载nvm二、安装nvm三、安装node.js总结NVM功能简介N

Qt 中集成mqtt协议的使用方法

《Qt中集成mqtt协议的使用方法》文章介绍了如何在工程中引入qmqtt库,并通过声明一个单例类来暴露订阅到的主题数据,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录一,引入qmqtt 库二,使用一,引入qmqtt 库我是将整个头文件/源文件都添加到了工程中进行编译,这样 跨平台

SpringBoot中使用 ThreadLocal 进行多线程上下文管理及注意事项小结

《SpringBoot中使用ThreadLocal进行多线程上下文管理及注意事项小结》本文详细介绍了ThreadLocal的原理、使用场景和示例代码,并在SpringBoot中使用ThreadLo... 目录前言技术积累1.什么是 ThreadLocal2. ThreadLocal 的原理2.1 线程隔离2

Linux内存泄露的原因排查和解决方案(内存管理方法)

《Linux内存泄露的原因排查和解决方案(内存管理方法)》文章主要介绍了运维团队在Linux处理LB服务内存暴涨、内存报警问题的过程,从发现问题、排查原因到制定解决方案,并从中学习了Linux内存管理... 目录一、问题二、排查过程三、解决方案四、内存管理方法1)linux内存寻址2)Linux分页机制3)

高效管理你的Linux系统: Debian操作系统常用命令指南

《高效管理你的Linux系统:Debian操作系统常用命令指南》在Debian操作系统中,了解和掌握常用命令对于提高工作效率和系统管理至关重要,本文将详细介绍Debian的常用命令,帮助读者更好地使... Debian是一个流行的linux发行版,它以其稳定性、强大的软件包管理和丰富的社区资源而闻名。在使用

SpringBoot使用minio进行文件管理的流程步骤

《SpringBoot使用minio进行文件管理的流程步骤》MinIO是一个高性能的对象存储系统,兼容AmazonS3API,该软件设计用于处理非结构化数据,如图片、视频、日志文件以及备份数据等,本文... 目录一、拉取minio镜像二、创建配置文件和上传文件的目录三、启动容器四、浏览器登录 minio五、