摘要:
Rime协议栈所有通信都是通过通道channel标识的,本文介绍通道channel结构体及相关函数,包括channel_init、channel_open、channel_close、channel_lookup、channel_set_attributes。
PS:channel有通道、信道、渠道、频道等含义,本文将其翻译为通道,理由是Rime中channel是逻辑上的,并不是真实的信道(信道指信号的传输媒质)。
一、概述
Rime协议栈所有通信都是通过通道channel标识的,即两个应用进程通信需要相同的channel。想想套接字编程,两个进程通信具有相同的端口号。
channel由两字节标识,小于128保留给系统使用(如shell、系统应用程序)。系统所有channel连成一个链表,channel结构体源码如下:
- struct channel
- {
- struct channel *next;
- uint16_t channelno;
- const struct packetbuf_attrlist *attrlist;
- uint8_t hdrsize;
- };
- struct packetbuf_attrlist
- {
- uint8_t type;
- uint8_t len;
- };
其示意图如下(值得注意的是attrlist指向的是packetbuf_attrlist数组):
channel链表示意图源文件 Rime协议栈通道channel.rar
图1 channel链表示意图
结构体channel各成员变量含义如下:
next
指向下一个channel,最后一个channel的next指向空。
channelno
用两字节标识不同的channel。
attrlist
指向由类型type和长度len两个成员变量组成的结构体数组packetbuf_attrlist[]。
hdrsize
hdrsize是指数据报的头部大小。即为packetbuf_attrlist类型的数组attrlist[]中所有结构体中成员变量的长度len的总和。详情见2.7。
二、相关操作
2.1 声明链表channel_list
宏LIST(name)用来声明一个结构体类型的链表,并且该结构体的第一个成员变量必须是指针,宏LIST用该指针形成链表。除些之外,该链表被声明为静态变量(不用导出符号,从而方便其他模块使用)。部分源码如下:
- //LIST(channel_list);
- #define LIST(name) \
- static void *LIST_CONCAT(name,_list) = NULL; \
- static list_t name = (list_t)&LIST_CONCAT(name,_list)
- typedef void ** list_t;
LIST_CONCAT宏用于连结两个字符串,源码如下:
- #define LIST_CONCAT(s1, s2) LIST_CONCAT2(s1, s2)
- #define LIST_CONCAT2(s1, s2) s1##s2
在C语言中,##表示连结(concatenate),#表示字符串化。那么,把上述语句翻译下,就是这样了:
- LIST(channel_list);
- static void *channel_list_list = NULL;
- static list_t channel_list = (list_t) &channel_list_list;
这里涉及到指针的指针,channel_list是指向channel_list_list的指针,channel_list_list指向NULL指针。见下图:
图2 channel_list示意图
2.2 channel初始化channel_init
LIST(channel_list)只是声明了链表,使用之前需先初始化,即channel_init。结合图2可知,channel_init实际上是将channel_list指向的值设为NULL(不再是channel_list_list)。源代码如下:
- void channel_init(void)
- {
- list_init(channel_list);
- }
- void list_init(list_t list)
- {
- *list = NULL;
- }
2.3 channel_open
channel_open用于打开一个通道,实际上设置通道号并将通道c加到channel链表中,源代码如下:
- void channel_open(struct channel *c, uint16_t channelno)
- {
- c->channelno = channelno;
- list_add(channel_list, c);
- }
list_add将节点加入到链表末尾,list_add源代码如下:
- void list_add(list_t list, void *item)
- {
- struct list *l;
- list_remove(list, item); /* Make sure not to add the same element twice */
- ((struct list*)item)->next = NULL;
- l = list_tail(list);
- if(l == NULL)
- {
- *list = item;
- }
- else
- {
- l->next = item;
- }
- }
2.4 channel_close
channel_close用于关闭一个通道,即从链表删除相应的channel。channel_close源代码如下:
- void channel_close(struct channel *c)
- {
- list_remove(channel_list, c);
- }
list_remove用于从链表移除指定的元素,源代码如下:
- void list_remove(list_t list, void *item)
- {
- struct list *l, *r;
- if(*list == NULL)
- {
- return ;
- }
- r = NULL;
- for(l = *list; l != NULL; l = l->next)
- {
- if(l == item)
- {
- if(r == NULL)
- {
- *list = l->next; /* First on list */
- }
- else
- {
- r->next = l->next; /* Not first on list */
- }
- l->next = NULL;
- return ;
- }
- r = l;
- }
- }
2.5 channel_lookup
查找通道号对应的channel,源代码如下:
- struct channel *channel_lookup(uint16_t channelno)
- {
- struct channel *c;
- for(c = list_head(channel_list); c != NULL; c = list_item_next(c))
- {
- if(c->channelno == channelno)
- {
- return c;
- }
- }
- return NULL;
- }
list_head用于返回链表的第一个元素,list_item_next用于返回当前元素的下一个元素,源代码如下:
- void *list_head(list_t list)
- {
- return *list;
- }
- void *list_item_next(void *item)
- {
- return item == NULL ? NULL : ((struct list*)item)->next;
- }
2.6 channel_set_attributes
channel_set_attributes用于设置通道的属性,首先用channel_lookup函数找到通道号对应的channel。而后设置attrlist、hdrsize成员变量。源代码如下:
- void channel_set_attributes(uint16_t channelno, const struct packetbuf_attrlist attrlist[])
- {
- struct channel *c;
- c = channel_lookup(channelno);
- if(c != NULL)
- {
- c->attrlist = attrlist;
- c->hdrsize = chameleon_hdrsize(attrlist);
- }
- }
chameleon_hdrsize用于计算channel的hdrsize,源代码如下:
- //chameleon_hdrsize(attrlist);
- int chameleon_hdrsize(const struct packetbuf_attrlist attrlist[])
- {
- return CHAMELEON_MODULE.hdrsize(attrlist);
- }
CHAMELEON_MODULE由一系列条件编译定义,如下:
- #ifndef CHAMELEON_MODULE
- #ifdef CHAMELEON_CONF_MODULE
- #define CHAMELEON_MODULE CHAMELEON_CONF_MODULE
- #else
- #define CHAMELEON_MODULE chameleon_bitopt
- #endif
- #endif
可见,可以自定义CHAMELEON_MODULE,即定义CHAMELEON_CONF_MODULE,默认情况是chameleon_bitopt,其源代码如下:
- CC_CONST_FUNCTION struct chameleon_module chameleon_bitopt =
- {
- unpack_header, pack_header, header_size
- };
chameleon_bitopt是chameleon_module结构体类型,源代码如下:
- struct chameleon_module
- {
- struct channel *(*input)(void);
- int(*output)(struct channel*);
- int(*hdrsize)(const struct packetbuf_attrlist*);
- };
可见,chameleon_module结构体包含3个函数指针,函数unpack_header、pack_header、header_size在core/net/rime/chameleon-bitopt.c实现,这里先不介绍,待用到再介绍。
2.7 hdrsize计算
透过2.6的分析,chameleon_hdrsize实际上是调用header_size函数。去除一些无关的注释,header_size源码如下:
- //chameleon_hdrsize(const struct packetbuf_attrlist attrlist[])
- static int header_size(const struct packetbuf_attrlist *a)
- {
- int size, len;
- size = 0;
-
- for(; a->type != PACKETBUF_ATTR_NONE; ++a)
- {
- #if CHAMELEON_WITH_MAC_LINK_ADDRESSES
- if(a->type == PACKETBUF_ADDR_SENDER || a->type == PACKETBUF_ADDR_RECEIVER)
- {
- continue;
- }
- #endif
- len = a->len;
- size += len;
- }
- return size;
- }
显然,channel的hdrsize为packetbuf_attrlist类型的数组attrlist[]中所有结构体中成员变量的长度len的总和。