RPMsg:协议简介

0. 起因

之前在RPC原理与FastRPC实现一文中介绍过RPC的原理,简而言之,RPC就是实现本地程序调用位于另一个地址空间的例程(routine)的一种技术手段,其基本架构如图0-1所示。

图0-1 RPC基本架构

由于RPC只是一种技术手段,并没有一个统一的标准,因此,每一种RPC框架根据其应用场景不同,所采用的实现方式也不尽相同。这些差异主要集中在两个方面:

  1. 数据的序列化和反序列化方法不同:例如可以使用JSON、XML以及谷歌推出的Protocol Buffer、Flat Buffer等格式作为数据传递格式,甚至是自己定义的一套格式;
  2. 数据传递方法不一样:例如可以通过HTTP、TCP等网络协议栈将数据发送出去。

今天主要介绍的是众多数据传递方式中的一种——RPMsg。

RPMsg,全称Remote Processor Messaging,它定义了异构多核处理系统(AMP,Asymmetric Multiprocessing)中核与核之间进行通信时所使用的标准二进制接口。

1. AMP

现在的芯片非常复杂,很多都是包含多个核,特别是片上系统(SoC),一颗芯片上不仅包含了很多个核心,并且很多核心都是异构的。例如手机里的芯片,就可能包含了CPU、GPU、DSP等不同的处理器单元。显然,这些不同架构的核心都有着他们各自的目的,例如,为了在端测实现高效的神经网络模型推理,现在的高端手机芯片基本都搭载了专门为神经网络这种密集计算的算法定制的运算单元。既然是不同单元,我们就不能等同的对待他们。

为了最大限度的发挥他们的性能,协同完成某一任务,不同的核心上面运行的系统可能各不相同,有些核心上面运行的通用系统例如Linux、Android等,另外一些核心上可能运行的就是实时操作系统(RTOS)等。这些不同架构的核心以及他们上面所运行的软件组合在一起,就成了异构多处理系统(Asymmetric Multiprocessing System)。

由于一般他们存在的目的都是协同的处理事情,因此在异构多处理系统中往往会形成主-从(Master-Slave)结构。主核上的系统先启动,并负责准备好运行环境,然后根据需要或者一定规则启动从核并对其进行管理。主-从核心上的系统都准备好之后,他们之间就通过IPC(Inter Processor Communication)方式进行通信,而RPMsg就是IPC中的一种。对于非通用的操作系统,它上面很可能是没有搭载传统的TCP/IP协议栈的,因此,当主核想要通过RPC的方式调用从核上的服务的时候,便不能使用一般的RPC框架所采用的网络通信方式。这时候类似于RPMsg这种专门用于核间通信的通信协议就派上了用场。

2. RPMsg

2.1. Linux中的RPMsg

在Linux内核代码中,RPMsg的代码主要位于drivers/rpmsg/下,文件之间的主要关系如图2-1所示。一开始Linux中只使用VirtIO作为该协议传输层,后来又增加了Glink、SMD等,Glink和SMD主要用于高通平台。

用户代码通过操纵rpmsg驱动,实现数据的收发操作。所有数据都在RPMsg总线上传递。

图2-1 Linux中RPMsg代码结构

2.2. 原理

在AMP系统中,主-从核心通过共享内存的方式进行通信,如图2-2所示。内存的管理由主核负责,在每个通信方向上都有两个缓冲区,分别是USED和AVAIL,这个缓冲区可以按照RPMsg中消息的格式分成一块一块链接形成一个环,如图2-3所示。


图2-2 AMP中主从通信方式
图2-3 RPMsg中的消息缓冲区示意图

当主核需要和从核进行通信的时候可以分为四步,如图2-4所示:

  1. 主核先从USED中取得一块内存;
  2. 将消息按照消息协议填充;
  3. 将该内存链接到AVAIL换中;
  4. 触发中断,通知从核有消息处理。
图2-4 主核发消息给从核

反过来,从核需要和主核通信的时候也类似,如图2-5所示:

  1. 主核先从AVAIL中取得一块内存;
  2. 将消息按照消息协议填充;
  3. 将该内存链接到USED换中;
  4. 触发中断,通知主核有消息处理。
图2-5 从核发消息给主核

2.3. 协议

既然是一种信息交换的协议,与TCP/IP类似,RPMsg协议也有分层,主要分为三层,分别是传输层、MAC层和物理层,如图2-6所示:


图2-6 RPMsg协议层

各层在Linux代码的对应情况如图2-7所示。

图2-7 RPMsg结构

并且,在rpmsg 总线上的消息都具有以下结构,包含消息头和数据两部分。消息头与TCP/IP协议的UDP包非常像,并且是固定的,如图2-8所示。


图2-8 RPMsg消息格式

该消息格式的定义位于drivers/rpmsg/virtio_rpmsg_bus.c中,具体定义如下。

struct rpmsg_hdr {
    u32 src;
    u32 dst;
    u32 reserved;
    u16 len;
    u16 flags;
    u8 data[];
} __packed;

3. API

虽然目前RPMsg并未形成相关的标准文档,但Linux内核中已经有了RPMsg的实现并给出了相关定义,OpenAMP也参照Linux中的定义做出了自己的实现。因此,这里对相关的API做些简单的介绍。可能在不久的将来,RPMsg可以从一个事实上的标准变成一个真正的标准,毕竟,TCP/IP 也是这么过来的嘛。

  1. RPMsg virtio主核初始化共享缓冲池(RPMsg virtio 从核不需要用到这个API):
void rpmsg_virtio_init_shm_pool(struct rpmsg_virtio_shm_pool *shpool,
              void *shbuf, size_t size)
  1. 初始化RPMsg virtio 设备:
int rpmsg_init_vdev(struct rpmsg_virtio_device *rvdev,
          struct virtio_device *vdev,
          rpmsg_ns_bind_cb ns_bind_cb,
          struct metal_io_region *shm_io,
          struct rpmsg_virtio_shm_pool *shpool)
  1. 销毁 RPMsg virtio 设备:
void rpmsg_deinit_vdev(struct rpmsg_virtio_device *rvdev)`
  1. 从RPMsg virtio设备中获取RPMsg设备:
struct rpmsg_device *rpmsg_virtio_get_rpmsg_device(struct rpmsg_virtio_device *rvdev)
  1. 创建 RPMsg 终端:
int rpmsg_create_ept(struct rpmsg_endpoint *ept,
           struct rpmsg_device *rdev,
           const char *name, uint32_t src, uint32_t dest,
           rpmsg_ept_cb cb, rpmsg_ns_unbind_cb ns_unbind_cb)
  1. 销毁 RPMsg 终端:
void rpmsg_destroy_ept(struct rpsmg_endpoint *ept)
  1. 检查本地RPMsg 终端是否已经与远程的终端绑定,以及是够已经准备好可以发送信息:
int is_rpmsg_ept_ready(struct rpmsg_endpoint *ept)
  1. 通过默认的RPMsg 终端发送信息:
int rpmsg_send(struct rpmsg_endpoint *ept, const void *data, int len)
  1. 通过制定的终端以以及目的地地址发送信息:
int rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len,
       uint32_t dst)
  1. 通过指定原地址和目的地址发送消息:
int rpmsg_send_offchannel(struct rpmsg_endpoint *ept,
            uint32_t src, uint32_t dst,
            const void *data, int len)
  1. 尝试通过默认的终端发送信息,如果当前没有缓存可用则返回:
int rpmsg_trysend(struct rpmsg_endpoint *ept, const void *data,
        int len)
  1. 尝试通过制定的终端以以及目的地地址发送信息,如果当前没有缓存可用则返回:
int rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, int len,
          uint32_t dst)
  1. 尝试通过指定原地址和目的地址发送消息,如果当前没有缓存可用则返回:
int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept,
               uint32_t src, uint32_t dst,
               const void *data, int len)
本文首发于个人微信公众号TensorBoy,微信扫描上方二维码或者微信搜索TensorBoy并关注,及时获取最新文章。C++ | Python | Linux | 原理 | 源码,有一起玩耍的么?

4. References

[1] https://elinux.org/images/3/3b/NOVAK_CERVENKA.pdf
[2] https://github.com/OpenAMP/open-amp/wiki/AMP-Intro
[3] https://github.com/NXPmicro/rpmsg-lite
[4] https://github.com/torvalds/linux/tree/master/drivers/rpmsg

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353