LiteIPC driver
Overview
LiteIPC is an OpenHarmony extension to the LiteOS(a) kernel which provides a means for processes on the same device to communicate with each other. It is a somewhat higher level mechanism than POSIX IPC methods such as message queues or shared memory, providing automatic management of resources used for IPC and control of access rights to send messages to other processes. IPC messages are exchanged between tasks. An IPC service is a task which has been set up to receive request type messages. Access rights are granted to processes, if a process has access to a service then all tasks in the process are able to send requests to the service.
API
Application-layer Interface
//foundation/communication/ipc/interfaces/innerkits/c/ipc/include/ipc_skeleton.h (mini and small systems)
//foundation/communication/ipc/interfaces/innerkits/c/ipc/include/serializer.h (mini and small systems)
//foundation/communication/ipc/ipc/native/c/ipc/src/liteos_a/include/lite_ipc.h (standard systems)
Implementation
//kernel/liteos_a/kernel/extended/liteipc/hm_liteipc.h
//kernel/liteos_a/kernel/extended/liteipc/hm_liteipc.c
LITEIPC_DRIVER
specifies the name of the character device used to communicate with the LiteIPC driver (currently /dev/lite_ipc).
Memory mapping allocates a memory area for storing messages received by the process' tasks. ioctl calls to the driver before it has been memory mapped return with error ENOMEM.
The IpcMsg structure is the basic unit of transaction for LiteIPC.
Member | Type | Description |
---|---|---|
type | MsgType | The message's type. It can be one of the following values MT_REQUEST, MT_REPLY, MT_FAILED_REPLY, MT_DEATH_NOTIFY. |
target | SvcIdentity | Specifies the message's recipient. |
flag | IpcFlag | Indicates whether the message can be replied to or is one-way. It can be one of the following values LITEIPC_FLAG_DEFAULT, LITEIPC_FLAG_ONEWAY. |
dataSz | UINT32 | The size of the message data in bytes pointed to by data (cannot exceed IPC_MSG_DATA_SZ_MAX, currently 1024 bytes). |
data | void* | Pointer to the message data. |
spObjNum | UINT32 | The number of special objects contained in the message data (cannot exceed IPC_MSG_OBJECT_NUM_MAX). |
offsets | void* | An array of offsets relative to data pointing to SpecialObj special objects in data. |
code | UINT32 | Service function code. |
timestamp | UINT64 | Timestamp for when the message was sent. Automatically set by the IPC system for requests and death notifications. |
taskID | UINT32 | Specifies the message's sender. Automatically set by the IPC system. |
processID | UINT32 | Additional information on the message's sender. Automatically set by the IPC system. |
SpecialObj
Member | Type | Description |
---|---|---|
type | ObjType | The type of special object. It can be one of the following values OBJ_FD, OBJ_PTR, OBJ_SVC. |
content | ObjContent | The special object. |
ObjContent
SpecialObj type | Member | Type | Description |
---|---|---|---|
OBJ_FD | fd | UINT32 | File descriptor referring to the file description to be sent. |
OBJ_PTR | ptr | BuffPtr | Auxiliary data to be sent (not limited by IPC_MSG_DATA_SZ_MAX). |
OBJ_SVC | svc | SvcIdentity | A service to give the recipient permission to send requests to. |
IPC_SEND_RECV_MSG
is the primary request which provides the ability to send and receive IpcMsg messages as well as free memory used by unneeded messages. Its argument is a pointer to an IpcContent structure.
IpcContent
Member | Type | Description |
---|---|---|
flag | UINT32 | Specifies the operation(s) to be performed. It is the bitwise-or of one or more of the following flags SEND, RECV, BUFF_FREE. |
outMsg | IpcMsg* | Points to a message to be sent. |
inMsg | IpcMsg* | Points to a message that has been received. |
buffToFree | void* | Points to IPC memory to be freed (a previously received message which is no longer needed). |
-
The SEND flag indicates a request to send outMsg. Returns with error EINVAL if any member of the message has been given an invalid value.
-
Sending a message with type MT_REQUEST returns with error EACCES if the task doesn't have access rights to the recipient, and EINVAL for an invalid recipient. All tasks have access rights to the CMS (see IPC_SET_CMS), and to their own process' IPC services.
-
Sending a message with type MT_REPLY or MT_FAILED_REPLY returns with error EINVAL if any of the following are not satisifed:
- buffToFree must point at the message being replied to and the BUFF_FREE flag must be set.
- The outMsg recipient (target.handle) must match the buffToFree sender (taskID).
- The outMsg and buffToFree timestamps must match.
- buffToFree must be a MT_REQUEST type message and cannot be marked as LITEIPC_FLAG_ONEWAY.
- buffToFree must be addressed to a service of the current process.
Trying to reply to a message sent more than LITEIPC_TIMEOUT_NS nanoseconds ago (currently 5 seconds) returns with error ETIME.
The message is copied into memory allocated from the IPC memory area of the process for the recipient task specified by target.handle. Returns with error ENOMEM if the memory cannot be allocated. Special objects in offsets are then processed. For each OBJ_FD, a file descriptor that refers to the specified file description is created for the recipient and the recipient's copy of the OBJ_FD is updated with the new file descriptor, discards the message and returns with error EPERM if the sender doesn't have permission to create the file descriptor, EBADF for a bad file descriptor, or ESRCH for other problem creating the file descriptor. OBJ_PTR objects' data is copied into memory allocated from the recipient's IPC memory area, discards the message and returns with error EINVAL if unable to allocate enough memory. OBJ_SVC objects grant the recipient access rights to the service specified by the object if the sender already has access, discards the message and returns with error EACCES if the sender doesn't have access or EINVAL if the service isn't running. When handle = -1 and token = 1, the service handle of the sending task is substituted after first creating one if needed. Access rights are not revokable, on error any access rights granted before the special object which caused the error will remain. The message is then added to the tail end of the recipient's list of received messages, the recipient is woken and the scheduler is called.
-
-
The BUFF_FREE flag indicates a request to free the memory used by the buffToFree message so it can be used again later for receiving messages. Returns with error EFAULT if buffToFree doesn't point to a received message. Returns with error EINVAL if an invalid address.
-
The RECV flag indicates a request to receive a message.
- If the SEND flag is set the task will wait for a message for up to LITEIPC_TIMEOUT_MS milliseconds (currently 5 seconds). Returns with error ETIME on timeout. Messages with a type of MT_REQUEST and MT_REPLY or MT_FAILED_REPLY type messages which don't match outMsg (matching timestamp or matching code and target.token depending on kernel configuration) are discarded (resets timer). Sets inMsg to point at the received message and removes it from the list of received messages for MT_REPLY or MT_DEATH_NOTIFY type messages. Note that receiving a MT_DEATH_NOTIFY makes it impossible to receive the reply so send/receive requests shouldn't be used by services which might receive death notifications. Discards the message and returns with error ENOENT without changing inMsg if the received message has type MT_FAILED_REPLY.
- If the SEND flag is not set, the task will sleep until a message with type MT_REQUEST or MT_DEATH_NOTIFY is received. Sets inMsg to point at the received message, and removes the message from its list of received messages. Discards MT_REPLY or MT_FAILED_REPLY type messages received while waiting.
For a single request the operations are processed in the order SEND->BUFF_FREE->RECV with BUFF_FREE being processed even if there was an error in SEND. Error checking on buffToFree occurs before SEND, an error in buffToFree will abort the request without doing anything.
The IPC_SET_IPC_THREAD
request designates the current task as the IPC task for the current process. It can only be performed once per process. Returns the ID of the task set as IPC task on success, subsequent calls return with error EINVAL. The IPC task receives the death notifications from IPC services the process has access rights to when those services terminate.
IPC_SEND_RECV_MSG and IPC_SET_IPC_THREAD both return with error EINVAL if the CMS has not been set.
The IPC_GET_VERSION
request (standard systems API only) gets the version of the LiteIPC driver. The argument to the request is a pointer to an IpcVersion structure. The driverVersion member is set to the LiteIPC driver version, major version in the low 16 bits and minor version in the high 16 bits.
Internal functions
The first task to use the IPC_SET_CMS
request sets itself as the system's CMS. OpenHarmony assigns this role to the distributed scheduler's samgr. Once set it cannot be changed. Returns with error EEXIST if the CMS has already been set. Messages are sent to the CMS by setting a recipient handle of 0. The argument to the request specifies the maximum size of a message sent to the CMS. Sending a message whose total size including IpcMsg data structure, data size, size of special object offsets, and data in any BuffPtr special objects exceeds the maximum size (currently 256 bytes) returns with error EINVAL.
The IPC_CMS_CMD
request provides various service related utility functions to the CMS. It can only be used by the CMS, any other task making this request will get an EACCES error. The argument to the request is a pointer to a CmsCmdContent structure. The cmd member indicates the function to be performed.
- CMS_GEN_HANDLE creates a service handle for the task specified by taskID member and stores the handle in the serviceHandle member. The CMS always has access rights to any created IPC services.
- CMS_REMOVE_HANDLE unregisters the service handle specified by the serviceHandle member.
- CMS_ADD_ACCESS gives the process of the task specified by the taskID member access rights to the service specified by the serviceHandle member.
LiteIPC includes utility functions for the kernel to manage the IPC system.
OsLiteIpcInit
initializes the IPC system and must be called before it can be used.
LiteIpcPoolReInit
creates and initializes the process control block IPC variables for a child process from its parent's process. Called by the kernel on the creation of child processes for basic initialization.
LiteIpcPoolDestroy
removes a process' IPC memory pool allocated by memory mapping and all the process' access rights. Also responsible for freeing memory, used for per-process IPC variables, which the system dynamically allocates the first time a process opens LITEIPC_DRIVER
. Called by the kernel on process deletion for automatic memory and IPC resource management.
LiteIpcRemoveServiceHandle
deregisters a service, clearing out the service task's message list and the list of processes with access rights to the service and sending death notification messages to any services with a set IPC task which had access. Death notification messages are only sent once, if there is an error in the send (ENOMEM) the recipient will not get the death notification. Death notification messages set target.token to the sevice handle of the service which terminated. Also responsible for freeing memory, used for per-task IPC variables, which the system dynamically allocates the first time a task sends a valid message or initiates a request including RECV. Called by the kernel on task deletion for automatic IPC resource management.
Sample code
-
Initialization before we can use LiteIPC.
#include "liteipc_adapter.h" #include "liteipc.h" int fd = open(LITEIPC_DRIVER, O_RDONLY); mmap(NULL, 10000, PROT_READ, MAP_PRIVATE, fd, 0);
-
Send a message to the CMS. For simplicity let's say we have a primitive CMS which simply gives us access to and returns the handle of the service named in the request we send to it.
#define SVCNAME "wakeup service" IpcMsg msg = { .type = MT_REQUEST, .target = { 0, 0, 0 }, .flag = LITEIPC_FLAG_DEFAULT, .dataSz = sizeof(SVCNAME), .data = SVCNAME, .spObjNum = 0, .offsets = NULL }; IpcContent content = { .flag = SEND | RECV, .outMsg = &msg }; // Send a message to the CMS if (ioctl(fd, IPC_SEND_RECV_MSG, &content) < 0) { goto ERROR; }
-
Send a message to the wakeup service.
struct { char summary[20]; SpecialObj command; SpecialObj svc; } wakeupData = { .summary = "one wakeup" }; char commandStr[100] = "Wake me up at 9:00 in the morning."; void* wakeupOffsets[2] = { (void*)((uintptr_t)&wakeupData.command - (uintptr_t)&wakeupData), (void*)((uintptr_t)&wakeupData.svc - (uintptr_t)&wakeupData) }; // Send a request to the wakeup service and free the CMS's reply content.flag = SEND | BUFF_FREE; content.buffToFree = content.inMsg; msg.type = MT_REQUEST; // Set the recipient from the CMS' reply msg.target.handle = *(uint32_t*)content.inMsg->data; // Add the auxiliary data. wakeupData.command.type = OBJ_PTR; wakeupData.command.content.ptr.buffSz = sizeof(commandStr); wakeupData.command.content.ptr.buff = commandStr; // Automatically handle IPC service setup on ourselves and give the // wakeup service access to send us requests. wakeupData.svc.type = OBJ_SVC; wakeupData.svc.content.svc.handle = -1; wakeupData.svc.content.svc.token = 1; // Complete the message and send it. msg.data = &wakeupData; msg.dataSz = sizeof(wakeupData); msg.offsets = wakeupOffsets; msg.spObjNum = 2; if (ioctl(fd, IPC_SEND_RECV_MSG, &content) < 0) { goto ERROR; }
-
Wait for wakeup and process the wakeup message.
// Enter "server" mode, wait for the service to wake us up. content.flag = RECV; ioctl(fd, IPC_SEND_RECV_MSG, &content); // Free the received message content.flag = BUFF_FREE; content.buffToFree = content.inMsg; ioctl(fd, IPC_SEND_RECV_MSG, &content);
-
LiteIPC automatically cleans up our IPC resources when we exit, but closing the file descriptor when we're done using it is a good habit.
ERROR: close(fd);