contiki之nbr_table.c

memb还是挺简单,可以看这篇博客:   内存分配之memb

懂了list和memb终于可以开始研究nbr_table了,虽然已经有人写过分析nbr_table的博客,可是看了之后仍然比较懵。这里先贴几张图,说明一下nbr_table的一些数据结构联系,理解了之后再去看代码会比较容易理解。

先来看一下nbr_table_t 结构体,这是这个模块的核心结构体。一个邻居列表就是一个结构体,既然是列表里面肯定就得有好几个成员了,这个邻居列表灵活就在于这个列表成员的数据类型是可变的,根据上层不同的需求可以定义不同类型的成员。这个结构体总共就四个成员,第一个成员看起来是索引,其实我觉得更应该理解为序号,后面待会会讲。第二个成员是size,指示了data指针指向的那个数组的数据类型长度是多少个字节,第三个成员是个函数指针,调用这个函数就可以删除数组里的某个成员,在你定义了一个邻居列表后就需要赋给这个成员一个具体的函数指针,最后一个成员就是指向邻居列表成员数组的指针了。

contiki之nbr_table.c

具体的邻居列表并没有在nbr_table模块中定义,因为列表成员类型不是固定的,所以在内存中开辟邻居列表结构这件事就是上层需要去做的事。那么nbr_table这个模块就只是为上层提供了管理这个邻居列表的功能。上层在内存开辟了一个邻居列表结构体后,就要调用nbr_table模块提供的注册函数,把这个结构体交给nbr_table模块管理。这个注册函数做的事还是挺简单的,同样上图。在nbr_table模块里定义了一个指针数组,叫all_tables,用来记录所以邻居列表结构体的指针。当调用nbr_table模块的注册函数的时候,nbr_table模块就会按顺序在all_tables中存放邻居列表结构体指针,并且把存放的位置写到邻居列表结构中的index变量,所以你会看到下图中三个邻居列表结构体的index变量依次递增,所以我才说这个index更像是序号,是nbr_table为每个注册的邻居列表结构体编的序号。

contiki之nbr_table.c

说完邻居列表,接下来讲另外一个结构体,这个结构体也是很重要,可以说nbr_table这模块就是围绕邻居列表和这个结构体展开的。这个结构体叫nbr_table_key_t。做啥用的呢,非常简单,就只是记录了一个网络地址而已,为啥叫key还真是不太明白。这个结构体如下图所示。结构体其实就两个成员,一个是指向本类型的结构体指针next,另外一个成员就是lladdr,16位的网络地址。nbr_table模块用MEMB宏定义开辟了一个nbr_table_key_t类型的数组,还有一个count数组。这个nbr_table_key_t数组之后会用之前讲过的list串起来成为一个链表。这个count是干啥用的呢,它跟nbr_table_key_t数组有一样多的成员个数,并且一一对应,一个count数组成员对应一个nbr_table_key_t成员,这个count就是用来记录某个nbr_table_key_t数组成员是否已经被使用了。注意了,这个nbr_table_key_t数组是nbr_table这个模块定义并且开辟了内存空间的,更重要的是它是一个static,静态类型数组,别的模块访问不了。

contiki之nbr_table.c


有了邻居列表有了上面这个网络地址数组,nbr_table模块要干嘛呢,答案就是把这两个数据绑定在一起。这个网络地址数组仅仅是标识出了邻居的地址而已,那么邻居的更具体的信息在哪呢?在邻居列表结构体里,而且具体是什么信息,是上层决定的,并且一个邻居可能有很多信息,这些信息被上层分门别类归纳在几个邻居列表结构体里都有可能,那么怎么把这些可能分散在几个邻居列表里的邻居信息跟这个邻居的网络地址数组一一对应起来呢。同样上图。

contiki之nbr_table.c

代码的作者用了一个简单直接的方法。让邻居列表里的成员数量跟网络地址数组的成员数量保持一致,然后一一对应起来,网络地址数组里第一个地址的邻居信息就在邻居列表的第一个成员里。这样子,不论你用多少个邻居列表来存储邻居信息都不会乱,反正一个地址的信息就是所有邻居列表的第一个成员。

讲到这里这个网络地址数组,即nbr_table_key_t数组,还只是一个数组而已,都还不是链表。那它什么时候才是链表呢,我们是用MEMB来开辟这个网络地址数组的,虽然我们知道它就是一个数组而已,但是用的时候却不允许这样用。用的时候是这样的,你需要存一个网络地址,那么你就要乖乖调用memb提供的函数,向这个网络地址数组申请一个成员来使用,当你得到一个成员后,你就要调用list提供的函数,把这个新得到的成员串到nbr_table_key_t链表上,当然这里面还涉及到如果你想存一个网络地址但是发现这个网络地址已经存在了或者网络地址数组没空间了这些问题,这里就不展开了。重点是,当你分配得到一个网络地址数组成员后,跟这个成员对应的邻居列表的成员将会被初始化,以待填入新的邻居信息。就是因为邻居列表和这个网络地址数组有这种关系,所以nbr_table模块里才会有那么多像key_from_index  item_from_key啦之类的函数,就是在根据这种对应关系,给定网络地址找出邻居列表成员,给定邻居列表成员找出网络地址等等。

nbr_table模块还有两个数组,used_map和locked_map,具体利用这个两个数组能达到什么目的我搞不清楚,但是我知道这两个数组跟邻居列表有怎么样的对应关系,上图。

contiki之nbr_table.c

这里画出了usedmap跟邻居列表的对应关系,lockedmap也是同样的关系。usedmap数组大小同样跟邻居列表成员一样多,而且同样一一对应,usedmap数组第一个成员对应邻居列表第一个成员,特别地在于,一个邻居列表成员只对应数组成员的一个位而已,至于是哪个位,由邻居列表里的index来决定,这个时候index才真的有索引的感觉。看到这里你一定会发现,如果是这样子的话,岂不是说系统里最多只能有八个邻居列表结构体,多了的话就没法跟usedmap对应了,正是如此。

如果把上面这些数据关系都理解透了,再看接下来两篇分析代码的博文就会容易很多了。

nbr_table源码分析(1)

nbr_table源码分析(2)