ePRC框架分析与应用

eRPC是一个开源的适用于多核嵌入式系统的远程过程调用框架,它专门为紧密耦合的系统设计,使用纯C语言实现远程功能,并且代码量小(<5KB)。本文从ePRC的上手使用开始,然后对其框架原理、开销和性能进行简单分析。

快速开始

eRPC由NXP创建,提供了一种简单的通过本地函数调用远程系统上的软件方法的机制。

其特点是:

  • 一种创建client/server应用的简单方法
  • 跨平台的erpcgen工具生成填充代码
  • 简单的传输层设计,它甚至提供了同样是NXP开发的rpmsg-lite的IPC传输层
  • Server可以是阻塞或非阻塞的

Remote procedure call system (RPC)

快速使用:

  1. 编写IDL文件

  2. 生成填充代码

  3. 导入eRPC公共文件和接口文件

  4. 添加传输层驱动

  5. 编写应用程序

QuickStart

IDL文件编写

创建的idl文件 idl_example.erpc

1
2
3
4
5
@output_dir("idl_example_source")
@types_header("idl_example_common.h")
program idl_example

import "idl_test.erpc"
  • program

    可选的program语句用于指定整个输入的名称。该名称用于输出文件名,最终的文件名为program_group_xxx。

  • import

    通过import关键字,包含另一个IDL文件。

  • 注解

    注解以”@”字符开头,参数以”()”写入。注解提供了一种方法,用以通知eRPC生成器有关代码部分的特定请求。

    • @output(string):表示生成文件将输出到string指定的目录
    • @types_header(string):表示所有的声明(如typedef和struct)将会被移至指定的头文件中。这样可以避免重复定义的错误

创建具体的接口定义文件 idl_test.erpc

1
2
3
4
5
6
7
const int32 matrix_size = 5
type Matrix = int32[maxtrix_size][matrix_size];
enum enumPrintType {
BUILT_IN_TYPES,
STRUCT_TYPE,
ARRAY_TYPE,
}
  • 内置类型
IDL C IDL C
bool bool uint8 uint8_t
int8 int8_t uint16 uint16_t
int16 int16_t uint32 uint32_t
int32 int32_t uint64 uint64_t
int64 int64_t float float
string char* double double
  • 数组
IDL C
int32[10] variable int32_t variable[10];
int32[10] [6] variable int32_t variable[10] [6];
  • 别名

    type _aliasName = _originalType

  • 常量

    使用const关键字定义常量

  • 枚举

    IDL的enum定义会生成对应的C枚举类型,区别在于输出的C枚举类型中会增加匹配名称的typedef

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct erpc_data_t {
int32_t val;
byref float b;
}

struct foo {
int32 discriminator;
union(discriminator) {
case BUILT_IN_TYPES:
int32 num
case STRUCT_TYPE:
erpc_data_t data
case ARRAY_TYPE:
Matrix m
} value
}
  • 结构体

    IDL文件定义的结构体,输出文件中会额外添加typedef别名。当结构体的成员声明前使用”byref”关键字时,结构的成员将通过引用序列化。

  • 联合体

    封装型的联合体:联合体是结构体的一部分,需要结构体的其他成员作为联合体的鉴定符。

    非封装的联合体:如果把联合体定义在全局空间,则需要使用@discriminator(string)指定鉴别符。

    鉴定符,也就是说要指定union具体是那种类型

1
2
3
4
struct A {
int32 discriminator;
unionType data @discriminator(discriminator);
}
1
2
3
4
5
6
7
8
9
@group("test")
@include("idl_example_common.h")
@idl(2)
interface PrintService {
oneway erpc_print(in int32 index);
test_binary_allDirectionLength(binary a @length(p1), in binary b, inout binary d, in uint32 p1) -> void;
test_list_allDirectionLength(list<int32> a @length(p1), inout list<int32> d @length(p2), uint32 p1, uint32 p2) -> void;
send_my_foo(foo f @retain) -> foo;
}
  • @group(string)

    接口可以通过group注解来进行组织,该名字会体现在输出文件中

  • @include(string)

    string是将要包含的头文件名

  • @id(number)

    设置interface/方法的ID号,该ID号必须唯一

  • Interface

    接口包含一个或多个函数,这个函数在客户端使用,在服务端实现

  • oneway

    表明该函数是单向函数

  • in/out/inout

    指定参数的方向

  • @length

    指定变量的长度

  • @retain

    阻止server释放变量的空间

eRPC框架

类图

erpc_class

程序流程图

uml

资源消耗

eRPC基础框架的代码空间约为3~4KB

FileName Code RO Data RW Data
erpc_basic_codec.o 624 192 0
erpc_client_manager.o 414 40 0
erpc_crc16.o 8 0 0
erpc_message_buffer.o 158 0 0
erpc_server.o 122 0 0
erpc_simple_server.o 514 44 0
erpc_port_freertos.o 30 0 0
erpc_client_setup.o 242 24 24
erpc_server_setup.o 162 0 24
erpc_setup_mbf_dynamic.o 76 32 8

需要指出的是,eRPC的框架代码虽然体积不大,但是生成的shim code体积还是比较大的。

序列化字节流分析

serialized_Bytes

  • Client->Server
    d0 00 5e 79 00 01 01 01 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00
    d0 00 5e 79:h.m_messageSize = 0x00d0, h.m_crc = 0x795e
    00 01 01 01:header = 0x01010100,其中kBasicCodecVersion = 0x01, service = 0x01, request = 0x01, messageType = kInvocationMessage
    01 00 00 00:sequence = 0x00000001
    后续的200个字节是2个554(row*col*element_size)的matrix实参。