Rime协议栈所有通信都是通过通道channel标识的,本文介绍通道channel结构体及相关函数,包括channel_initchannel_openchannel_closechannel_lookupchannel_set_attributes

PS:channel有通道、信道、渠道、频道等含义,本文将其翻译为通道,理由是Rime中channel是逻辑上的,并不是真实的信道(信道指信号的传输媒质)。

1. 概述

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数组):

img

图1 channel链表示意图

channel链表示意图源文件:Rime协议栈通道channel.vsd

结构体channel各成员变量含义如下:

next

指向下一个channel,最后一个channel的next指向空。

channelno

用两字节标识不同的channel。

attrlist

指向由类型type和长度len两个成员变量组成的结构体数组packetbuf_attrlist[]

hdrsize

hdrsize是指数据报的头部大小。即为packetbuf_attrlist类型的数组attrlist[]中所有结构体中成员变量的长度len的总和。详情见2.7。

2. 相关操作

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指针。见下图:

img

图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用于关闭一个通道,即从链表删除相应的channelchannel_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。而后设置attrlisthdrsize成员变量。源代码如下:

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_bitoptchameleon_module结构体类型,源代码如下:

struct chameleon_module
{
  struct channel *(*input)(void);
  int(*output)(struct channel*);
  int(*hdrsize)(const struct packetbuf_attrlist*);
};

可见,chameleon_module结构体包含3个函数指针,函数unpack_headerpack_headerheader_sizecore/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的hdrsizepacketbuf_attrlist类型的数组attrlist[]中所有结构体中成员变量的长度len的总和。

本文系Spark & Shine原创,转载需注明出处本文最近一次修改时间 2022-03-22 14:49

results matching ""

    No results matching ""