在 ERPC 的 IDL 说明文档中,对于接口内存的申请和释放是这样描述的:
- On the client side: All memory space has to be allocated and provided by the user code. The shim code only reads from or writes into this memory space.
- On the server side: All memory space is allocated and provided by the shim code. The user code only reads from or writes into this memory space.
为了避免在实际使用中产生内存泄漏,还是需要实际查看代码来理解。
其实这里分两种情况: 1. 以指针作为返回参数 2. 以指针作为形参
这一部分的具体实现,需要查看生成的 server 和 client
端的代码来理解,还是以 ERPC
使用体验 的生成代码来看,其 API 为: 1
binary_t * DemoHello(const binary_t * val);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
static void read_binary_t_struct(erpc::Codec * codec, binary_t * data)
{
uint8_t * data_local;
codec->readBinary(&data->dataLength, &data_local);
data->data = (uint8_t *) erpc_malloc(data->dataLength * sizeof(uint8_t));
if (data->data == NULL)
{
codec->updateStatus(kErpcStatus_MemoryError);
}
else
{
memcpy(data->data, data_local, data->dataLength);
}
}
static void free_binary_t_struct(binary_t * data)
{
if (data->data)
{
erpc_free(data->data);
}
}
erpc_status_t DEMO_service::DemoHello_shim(Codec * codec, MessageBufferFactory *messageFactory, uint32_t sequence)
{
erpc_status_t err = kErpcStatus_Success;
binary_t *val = NULL;
// 申请形参的内存
val = (binary_t *) erpc_malloc(sizeof(binary_t));
if (val == NULL)
{
codec->updateStatus(kErpcStatus_MemoryError);
}
binary_t * result = NULL;
// startReadMessage() was already called before this shim was invoked.
read_binary_t_struct(codec, val);
err = codec->getStatus();
if (err == kErpcStatus_Success)
{
// Invoke the actual served function.
nestingDetection = true;
// 执行 API
result = DemoHello(val);
nestingDetection = false;
// preparing MessageBuffer for serializing data
err = messageFactory->prepareServerBufferForSend(codec->getBuffer());
}
if (err == kErpcStatus_Success)
{
// preparing codec for serializing data
codec->reset();
// Build response message.
codec->startWriteMessage(kReplyMessage, kDEMO_service_id, kDEMO_DemoHello_id, sequence);
write_binary_t_struct(codec, result);
err = codec->getStatus();
}
// 释放形参内存中 data 所指向的内存
if (val)
{
free_binary_t_struct(val);
}
// 释放形参本身的内存
if (val)
{
erpc_free(val);
}
// 释放返回值内存中 data 所指向的内存
if (result)
{
free_binary_t_struct(result);
}
// 释放返回值本身的内存
if (result)
{
erpc_free(result);
}
return err;
}
那么对于服务端的用户实现的代码,就需要: 1. 对于形参 -
如果服务端形参是读指针,那么只需要直接读取即可 -
如果服务端形参是写指针,那么用户需要申请其内部data
指针所指向的内存
2. 对于返回 -
用户需要完成对binary_t
以及其内部data
指针所指向的内存的申请
所以在server
端用户代码就是这样写的: 1
2
3
4
5
6
7
8
9
10
11binary_t * DemoHello(const binary_t * val) {
std::cout << "client message: " << (char*)val->data;
int len = std::strlen(ret) + 1;
char* buf = (char*)std::malloc(len);
std::strncpy(buf, ret, len - 1);
binary_t* message = new binary_t{(uint8_t*)buf, (uint32_t)(len - 1)};
return message;
}DemoHello
函数执行完毕后,申请的这两段内存是会被自动释放掉的。
客户端
客户端其自动生成代码如下: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52static void read_binary_t_struct(erpc::Codec * codec, binary_t * data)
{
uint8_t * data_local;
codec->readBinary(&data->dataLength, &data_local);
data->data = (uint8_t *) erpc_malloc(data->dataLength * sizeof(uint8_t));
if (data->data == NULL)
{
codec->updateStatus(kErpcStatus_MemoryError);
}
else
{
memcpy(data->data, data_local, data->dataLength);
}
}
// DEMO interface DemoHello function client shim.
binary_t * DemoHello(const binary_t * val)
{
erpc_status_t err = kErpcStatus_Success;
binary_t * result = NULL;
//......
// 申请读取内存
result = (binary_t *) erpc_malloc(sizeof(binary_t));
if (result == NULL)
{
codec->updateStatus(kErpcStatus_MemoryError);
}
// 这里会申请读取数据内存
read_binary_t_struct(codec, result);
err = codec->getStatus();
//......
// Dispose of the request.
g_client->releaseRequest(request);
// Invoke error handler callback function
g_client->callErrorHandler(err, kDEMO_DemoHello_id);
pre_post_action_cb postCB = g_client->getPostCB();
if (postCB)
{
postCB();
}
return result;
}binary_t
以及其内部data
指针所指向的内存的申请。
所以: 1. 客户端在接收到数据并处理后,需要主动释放器返回指针的内存!
所以,客户端的代码也是这样写的: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28int main(int argc, char *argv[]) {
// 创建客户端
auto transport = erpc_transport_tcp_init("127.0.0.1", 60901, false);
// 初始化消息缓存
auto message_buffer_factory = erpc_mbf_dynamic_init();
// 加入消息
erpc_client_init(transport, message_buffer_factory);
auto message = "Hello, this is client!\n";
binary_t cmd{(uint8_t*)message, (uint32_t)(std::strlen(message))};
binary_t *ret = DemoHello(&cmd);
std::cout << "Get message of server: " << ret->data << "\n";
if (ret->data) {
erpc_free(ret->data);
}
if (ret) {
erpc_free(ret);
}
// 关闭连接
erpc_transport_tcp_close();
return 0;
}
注意
以上是 ERPC
针对binary_t
而言,它会主动释放其内部的data
缓存,因为这是它内置类型。
而如果是用户自定义的结构体指针中还包含其他数据指针,就需要小心管理了。
所以为了避免这种情况: 1. 简单数据类型,使用结构体进行传递 2.
复杂的数据,通过binary_t
进行传递