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

相关文章

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

软考系统规划与管理师考试证书含金量高吗?

2024年软考系统规划与管理师考试报名时间节点: 报名时间:2024年上半年软考将于3月中旬陆续开始报名 考试时间:上半年5月25日到28日,下半年11月9日到12日 分数线:所有科目成绩均须达到45分以上(包括45分)方可通过考试 成绩查询:可在“中国计算机技术职业资格网”上查询软考成绩 出成绩时间:预计在11月左右 证书领取时间:一般在考试成绩公布后3~4个月,各地领取时间有所不同

安全管理体系化的智慧油站开源了。

AI视频监控平台简介 AI视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程,实现芯片、算法、应用的全流程组合,从而大大减少企业级应用约95%的开发成本。用户只需在界面上进行简单的操作,就可以实现全视频的接入及布控。摄像头管理模块用于多种终端设备、智能设备的接入及管理。平台支持包括摄像头等终端感知设备接入,为整个平台提

【Linux】应用层http协议

一、HTTP协议 1.1 简要介绍一下HTTP        我们在网络的应用层中可以自己定义协议,但是,已经有大佬定义了一些现成的,非常好用的应用层协议,供我们直接使用,HTTP(超文本传输协议)就是其中之一。        在互联网世界中,HTTP(超文本传输协议)是一个至关重要的协议,他定义了客户端(如浏览器)与服务器之间如何进行通信,以交换或者传输超文本(比如HTML文档)。

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

【Go】go连接clickhouse使用TCP协议

离开你是傻是对是错 是看破是软弱 这结果是爱是恨或者是什么 如果是种解脱 怎么会还有眷恋在我心窝 那么爱你为什么                      🎵 黄品源/莫文蔚《那么爱你为什么》 package mainimport ("context""fmt""log""time""github.com/ClickHouse/clickhouse-go/v2")func main(

Sentinel 高可用流量管理框架

Sentinel 是面向分布式服务架构的高可用流量防护组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。 Sentinel 具有以下特性: 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应

2024.9.8 TCP/IP协议学习笔记

1.所谓的层就是数据交换的深度,电脑点对点就是单层,物理层,加上集线器还是物理层,加上交换机就变成链路层了,有地址表,路由器就到了第三层网络层,每个端口都有一个mac地址 2.A 给 C 发数据包,怎么知道是否要通过路由器转发呢?答案:子网 3.将源 IP 与目的 IP 分别同这个子网掩码进行与运算****,相等则是在一个子网,不相等就是在不同子网 4.A 如何知道,哪个设备是路由器?答案:在 A

NGINX轻松管理10万长连接 --- 基于2GB内存的CentOS 6.5 x86-64

转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=190176&id=4234854 一 前言 当管理大量连接时,特别是只有少量活跃连接,NGINX有比较好的CPU和RAM利用率,如今是多终端保持在线的时代,更能让NGINX发挥这个优点。本文做一个简单测试,NGINX在一个普通PC虚拟机上维护100k的HTTP

PMBOK® 第六版 规划进度管理

目录 读后感—PMBOK第六版 目录 规划进度管理主要关注为整个项目期间的进度管理提供指南和方向。以下是两个案例,展示了进度管理中的复杂性和潜在的冲突: 案例一:近期,一个长期合作的客户因政策要求,急需我们为多家医院升级一个小功能。在这个过程中出现了三个主要问题: 在双方确认接口协议后,客户私自修改接口并未通知我们,直到催进度时才发现这个问题关于UI设计的部分,后台开发人员未将其传递给