在 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.
为了避免在实际使用中产生内存泄漏,还是需要实际查看代码来理解。
其实这里分两种情况:
以指针作为返回参数
以指针作为形参
这一部分的具体实现,需要查看生成的 server 和 client 端的代码来理解,还是以 ERPC 使用体验 的生成代码来看,其 API 为:
1 binary_t * DemoHello (const binary_t * val) ;
那么这里就主要观察 binary_t 这种内置类型。
服务端 服务端生成的代码如下:
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 ; read_binary_t_struct (codec, val); err = codec->getStatus (); if (err == kErpcStatus_Success) { #if ERPC_NESTED_CALLS_DETECTION nestingDetection = true ; #endif result = DemoHello (val); #if ERPC_NESTED_CALLS_DETECTION nestingDetection = false ; #endif err = messageFactory->prepareServerBufferForSend (codec->getBuffer ()); } if (err == kErpcStatus_Success) { codec->reset (); codec->startWriteMessage (kReplyMessage, kDEMO_service_id, kDEMO_DemoHello_id, sequence); write_binary_t_struct (codec, result); err = codec->getStatus (); } if (val) { free_binary_t_struct (val); } if (val) { erpc_free (val); } if (result) { free_binary_t_struct (result); } if (result) { erpc_free (result); } return err; }
从上面的代码可以看出来:
服务端会自动申请形参的内存,以及形参内部数据指针的内存
服务端会自动释放形参相关的所有内存
服务端会自动释放返回值相关的所有内存
那么对于服务端的用户实现的代码,就需要:
对于形参
如果服务端形参是读指针,那么只需要直接读取即可
如果服务端形参是写指针,那么用户需要申请其内部data
指针所指向的内存
对于返回
用户需要完成对binary_t
以及其内部data
指针所指向的内存的申请
所以在server
端用户代码就是这样写的:
1 2 3 4 5 6 7 8 9 10 11 binary_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 52 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); } } 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 (); g_client->releaseRequest (request); g_client->callErrorHandler (err, kDEMO_DemoHello_id); #if ERPC_PRE_POST_ACTION pre_post_action_cb postCB = g_client->getPostCB (); if (postCB) { postCB (); } #endif return result; }
可以看到,客户端代码是会自动完成对binary_t
以及其内部data
指针所指向的内存的申请。
所以:
客户端在接收到数据并处理后,需要主动释放器返回指针的内存 !
所以,客户端的代码也是这样写的:
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 int 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
缓存,因为这是它内置类型。
而如果是用户自定义的结构体指针中还包含其他数据指针,就需要小心管理了。
所以为了避免这种情况:
简单数据类型,使用结构体进行传递
复杂的数据,通过binary_t
进行传递