Contiki协议栈Rime:缓冲区管理packetbuf management
来源:互联网 发布:php浏览次数 编辑:程序博客网 时间:2024/05/09 01:16
更多的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(); // 先清空属性数组和packetbuf l = 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
intpacketbuf_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
voidpacketbuf_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_tpacketbuf_hdrlen(void){ return bufptr + hdrlen;}
返回数据部分长度
packetbuf_totlen
uint16_tpacketbuf_totlen(void){ return packetbuf_hdrlen() + packetbuf_datalen();}
返回头部和数据部分总长度
packetbuf_holds_broadcast
intpacketbuf_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
- Rime 协议栈缓冲区 packetbuf
- Contiki——Rime缓冲区packetbuf分析
- Contiki网络协议栈uIP和rime
- Contiki协议栈Rime:引子introduction
- Contiki协议栈Rime:包属性packetbuf_attr
- Contiki协议栈Rime:通道channel
- Contiki协议栈Rime:变色龙接口chameleon
- Contiki协议栈Rime:匿名广播abc
- Contiki协议栈Rime:广播ibc
- Contiki协议栈Rime: 节点链接地址linkaddr
- Contiki协议栈Rime:原始变色龙chameleon-raw
- Contiki协议栈Rime:头部转换模块chameleon-bitopt
- Contiki协议栈Rime:实验ibc和abc相互通信
- contiki的rime协议分析-abc
- contiki的rime协议分析-channel
- Contiki packetbuf原理
- Contiki packetbuf原理
- 使用scrapy爬取域名的whois信息
- Ubuntu FastDFSv5.05+Nginx1.9.15 环境搭建
- springmvc+jquery使用itext生存pdf文件
- MVC框架下分布视图重新加载
- 动态在RadioGroup加载RadioButton
- Contiki协议栈Rime:缓冲区管理packetbuf management
- Android FragmentTabHost轻松实现微博主界面(一)
- 调用Android手机相册功能并获取图片路径
- 一步步学spark之一scala面向对象之Object对象2.2
- ppt资料
- POJ 1002 487-3279
- [Matlab心得] 搭建最基本的S-function模块
- Spring-MVC入门
- 业务逻辑