One hundred blog Analysis | this article is: (message encapsulation) | analyze the communication content of LiteIpc process
Related articles of process communication are:
- v26.08 Hongmeng kernel source code analysis (spin lock) | be a good comrade of chastity archway
- v27.05 Hongmeng kernel source code analysis (mutex lock) | it's also a lock. It's really fuller
- v28.04 Hongmeng kernel source code analysis (process communication) | nine inter process communication methods
- v29.05 Hongmeng kernel source code analysis (semaphore) | who is solving the synchronization between tasks
- v30.07 Hongmeng kernel source code analysis (event control) | how to synchronize many to many tasks
- v33.03 Hongmeng kernel source code analysis (message queue) | how to transfer big data asynchronously between processes
- v76.01 Hongmeng kernel source code analysis (shared memory) | the fastest communication mode between processes
- v77.01 Hongmeng kernel source code analysis (message encapsulation) | analyze the communication content of LiteIpc process
Basic concepts
LiteIPC is a new IPC (inter process communication) mechanism provided by OpenHarmony LiteOS-A kernel. It is a lightweight inter process communication component and provides inter process communication capability for service-oriented system service framework. It is divided into two parts: Kernel Implementation and user mode implementation. The kernel implementation completes inter process messaging, IPC memory management Timeout notification, death notification and other functions; User mode provides serialization and deserialization capabilities, and completes the distribution of IPC callback message and death message.
We mainly explain the kernel state implementation part. I wanted to finish one article, but I found that it is much more complex and important than expected. Therefore, we divided it into two parts: communication content and communication mechanism. The content of communication is the message. There are more than 10 structures around the message. If we can't figure out the relationship between them, we must not understand the communication mechanism, so we have to figure out the relationship first and then the process. The following figure is the message encapsulation diagram drawn by the author after reading the LiteIPC module. It can be said that LiteIPC is the module with the most structures involved in the kernel. Please digest and understand. This article will focus on it.
As mentioned many times in the series, each module of the kernel is expanded around at least one important structure. If you grasp it, you can wipe the details clearly. In LiteIPC, this structure is IpcMsg.
operating mechanism
typedef struct {//IPC message structure MsgType type; /**< cmd type, decide the data structure below | The command type determines the following data structure*/ SvcIdentity target; /**< serviceHandle or targetTaskId, depending on type | Varies by command type*/ UINT32 code; /**< service function code | Service function code*/ UINT32 flag; ///< label #if (USE_TIMESTAMP == 1) UINT64 timestamp; ///< timestamp for verification #endif UINT32 dataSz; /**< size of data | Message content size*/ VOID *data; ///< message content, the message to be delivered. This data content refers to the content of spObjNum data. The positioning depends on offsets UINT32 spObjNum; ///< number of objects, for example, when spObjNum = 3, offsets = [0,35,79], which means reading 0 - 35 from data to the first object, and so on VOID *offsets; ///< offset. Note that there will be as many offsets as there are spObjNum. See CopyDataFromUser for details UINT32 processID; /**< filled by kernel, processId of sender/reciever | The process ID provided by the kernel to send / receive messages*/ UINT32 taskID; /**< filled by kernel, taskId of sender/reciever | The task ID provided by the kernel to send / receive messages*/ #ifdef LOSCFG_SECURITY_CAPABILITY UINT32 userID; ///< user ID UINT32 gid; ///< group ID #endif } IpcMsg;
unscramble
- First, the essence of communication is that you come and go. Of course, exceptions should also be considered
typedef enum { MT_REQUEST, ///< request MT_REPLY, ///< reply MT_FAILED_REPLY,///< reply failed MT_DEATH_NOTIFY,///< notice of death MT_NUM } MsgType;
- The second target, LiteIPC, has two main concepts: ServiceManager and Service. The whole system can only have one ServiceManager, while the Service can have multiple. ServiceManager has two main functions: one is responsible for the registration and logoff of services, and the other is responsible for managing the access rights of services (only authorized tasks can send IPC messages to the corresponding services). First, register the Task that needs to receive IPC messages as a Service through ServiceManager, and then configure access permissions for the Service Task through ServiceManager, that is, specify which tasks can send IPC messages to the Service Task. The core idea of LiteIPC is to maintain an IPC message queue for each Service Task in the kernel state. The message queue provides the upper user state program with read operations on behalf of receiving IPC messages and write operations on behalf of sending IPC messages through LiteIPC device files.
///SVC(service) service ID card typedef struct { UINT32 handle; //service ID, range [0, maximum task ID] UINT32 token; //Brought in by the application layer UINT32 cookie; //Brought in by the application layer } SvcIdentity;
- code and timestamp are set by the application layer to ensure that the reply is correct and valid. See checkreceivedmsg for details
- dataSz, data, spObjNum and offsets need to be understood together, which is the top priority. In fact, messages are divided into three types (objects)
These three objects are packaged in data. The total length is dataSz, spObjNum represents the number, and offsets is an integer array that marks the position of the corresponding object in data, so it is easy to read the data of the object from data. UINT32 fd type object communication is realized by sharing the same fd between two processes. The specific implementation function is HandleFd.typedef enum { OBJ_FD, ///Handle file OBJ_PTR, ///< pointer OBJ_SVC ///< service to set permissions } ObjType; typedef union { UINT32 fd; ///< file descriptor BuffPtr ptr; ///< start address of cache, i.e. pointer. When messages come from user space, the content shall be copied to kernel space SvcIdentity svc; ///< service to set access rights } ObjContent; typedef struct { // Ipcmsg - > data contains three sub messages, which should also be read into kernel space ObjType type; ///< type ObjContent content;///< content } SpecialObj;
SvcIdentity svc is used to set mutual access permissions between processes < - > tasks. The specific implementation function is HandleSvc.///Handled as a handle, the parameter processID is often not the current process LITE_OS_SEC_TEXT STATIC UINT32 HandleFd(UINT32 processID, SpecialObj *obj, BOOL isRollback) { int ret; if (isRollback == FALSE) { // No rollback ret = CopyFdToProc(obj->content.fd, processID);//The purpose is to point two different processes fd to the same system fd and share the feeling of fd if (ret < 0) {//Returns the new fd of the processID return ret; } obj->content.fd = ret; // Record the new FD of processID, which can be used for rollback } else {// Close process FD on rollback ret = CloseProcFd(obj->content.fd, processID); if (ret < 0) { return ret; } }
BuffPtr ptr transfers values through pointers. The specific implementation function is HandlePtr, and the corresponding structure is BuffPtr.///It is processed as a service. It is inferred here that Svc should be the abbreviation of service @ note_thinking LITE_OS_SEC_TEXT STATIC UINT32 HandleSvc(UINT32 dstTid, const SpecialObj *obj, BOOL isRollback) { UINT32 taskID = 0; if (isRollback == FALSE) { if (IsTaskAlive(obj->content.svc.handle) == FALSE) { PRINT_ERR("Liteipc HandleSvc wrong svctid\n"); return -EINVAL; } if (HasServiceAccess(obj->content.svc.handle) == FALSE) { PRINT_ERR("Liteipc %s, %d\n", __FUNCTION__, __LINE__); return -EACCES; } if (GetTid(obj->content.svc.handle, &taskID) == 0) {//Get the task to which the parameter message service ID belongs if (taskID == OS_PCB_FROM_PID(OS_TCB_FROM_TID(taskID)->processID)->ipcInfo->ipcTaskID) {//If the task ID is the same, that is, the task ID is ServiceManager AddServiceAccess(dstTid, obj->content.svc.handle); } } } return LOS_OK; }
typedef struct { UINT32 buffSz; ///< size VOID *buff; ///< content kernel needs to copy content from user space to kernel space } BuffPtr; ///Handle in pointer mode LITE_OS_SEC_TEXT STATIC UINT32 HandlePtr(UINT32 processID, SpecialObj *obj, BOOL isRollback) { VOID *buf = NULL; UINT32 ret; if ((obj->content.ptr.buff == NULL) || (obj->content.ptr.buffSz == 0)) { return -EINVAL; } if (isRollback == FALSE) { if (LOS_IsUserAddress((vaddr_t)(UINTPTR)(obj->content.ptr.buff)) == FALSE) { // Determine whether it is a user space address PRINT_ERR("Liteipc Bad ptr address\n"); //When not in user space return -EINVAL; } buf = LiteIpcNodeAlloc(processID, obj->content.ptr.buffSz);//Allocate memory in kernel space to receive data from user space if (buf == NULL) { PRINT_ERR("Liteipc DealPtr alloc mem failed\n"); return -EINVAL; } ret = copy_from_user(buf, obj->content.ptr.buff, obj->content.ptr.buffSz);//Copy data from user space to kernel space if (ret != LOS_OK) { LiteIpcNodeFree(processID, buf); return ret; }//Here we need to explain obj - > content ptr. Although the buff changes are all user space addresses, the meaning has changed for the second time. Although the data is the same, it points to the kernel space after the application is copied obj->content.ptr.buff = (VOID *)GetIpcUserAddr(processID, (INTPTR)buf);//Obtain the user space address of the process processID, so that the user space operation buf actually operates in the kernel space EnableIpcNodeFreeByUser(processID, (VOID *)buf);//Create an IPC node and hang it on the available linked list for reading } else { (VOID)LiteIpcNodeFree(processID, (VOID *)GetIpcKernelAddr(processID, (INTPTR)obj->content.ptr.buff));//Release IPC node in kernel space } return LOS_OK; }
- The processID and taskID are filled in by the kernel. The application layer is not aware of the process and task. It is exposed to the service ID, svcidentity Handle, when the upper layer is used, it only needs to send / read messages to the service, and the service is created by the kernel and bound to tasks and processes. Therefore, as long as there is a service ID, you can query the corresponding process and task ID.
- userID and gid involve user and group security modules. Please check the relevant articles in the series.
Processes and tasks
In addition, the two structures ProcIpcInfo and IpcTaskInfo LiteIPC realize the communication between processes, so there must be its location in the process control block, that is, ProcIpcInfo.
typedef struct { IpcPool pool; ///< IPC memory pool, which provides all memory related to kernel space allocation for IPC operation UINT32 ipcTaskID; ///< specify the task ID of ServiceManager LOS_DL_LIST ipcUsedNodelist;///< the linked list of nodes has been used, and the ippusednode node is hung on it. The memory of the applied ippusednode comes from the kernel heap space UINT32 access[LOSCFG_BASE_CORE_TSK_LIMIT]; ///< which tasks are allowed to be accessed by the process through IPC } ProcIpcInfo;
The process is just a housekeeper. What really makes the kernel busy is the task. There should also be a place for LiteIPC in the task control block, that is, IpcTaskInfo.
typedef struct { LOS_DL_LIST msgListHead;///< the IPC nodes are hung one by one, and the ipplistnodes are hung on them. The memory of the applied ipplistnodes comes from the process IPC memory pool BOOL accessMap[LOSCFG_BASE_CORE_TSK_LIMIT]; ///< should loscfg be used here_ BASE_ CORE_ PROCESS_ LIMIT ? @ note_ thinking ///< can the task send IPC messages to other processes } IpcTaskInfo;
The two structures are not complex. Hang the messages sent / replied to the corresponding linked list and provide mutual access functions between processes < - > tasks access and accessMap. Who will set the permissions? As mentioned above, it's HandleSvc.
IPC memory pool
And the last structure, IpcPool,
typedef struct {//Message passing in user space and kernel space is calculated by offset VOID *uvaddr; ///< user space address, the address mapped from kvaddr. The relationship between these two addresses must be clear, otherwise the core idea of IPC cannot be understood VOID *kvaddr; ///< kernel space address. IPC applies for kernel space, but it will map this address to user space through DoIpcMmap UINT32 poolSize; ///< IPC pool size } IpcPool;
It is the basis for LiteIPC to realize the communication mechanism. It is a very clever place for kernel design. It realizes the function of reading kernel state data in user state. Think about how it does it?
Bai Wen said that the core | grasp the main context
- Baiwen is equivalent to touching out the muscle and organ system of the kernel, which makes people start to feel plump and three-dimensional. Because it starts directly from the annotation source code, it is often sorted out every experience in the annotation process, and the following articles are slowly formed. The content is based on the source code and often uses life scenes as an analogy. Put the kernel knowledge points into a certain scene as much as possible, which has a sense of picture and is easy to understand and remember. It's important to say what others can understand! A hundred blogs are by no means Baidu's dogmatic talking about a bunch of awkward concepts, which is meaningless. More hope to make the kernel lifelike and feel more cordial.
- Just as the code needs to be debug ged constantly, there will be many errors and omissions in the content of the article. Please forgive me, but it will be corrected repeatedly and updated continuously, V * XX represents the article serial number and the number of modifications. It is carefully crafted, concise and comprehensive, and strives to create high-quality content.
- Bai Wen is posted at the "Hong Meng research station", "51CTO China CSDN", "the official account".
By function module:
- Cause and effect > > General catalogue | Scheduling story | Memory master slave | Source code comments | Source code structure | Static site | Reference documents |
- Basic Tools > > Bidirectional linked list | Bitmap management | Stack mode | timer | Atomic operation | time management |
- Load run > > ELF format | ELF parsing | Static link | Relocation | Process Image |
- Process Management > > Process management | Process concept | Fork | Special process | Process recycling | Signal production | Signal consumption | Shell editing | Shell parsing |
- Build > > Compiling environment | Compilation process | Environment script | Build tools | gn application | ninja Ninja |
- Process communication > > Spin lock | mutex | Process communication | Semaphore | Event control | Message queue | Shared memory | Message encapsulation |
- Memory management > > memory allocation | memory management | Memory assembly | Memory mapping | Memory Rules | physical memory |
- Task management > > Clock task | task scheduling | task management | Scheduling queue | Scheduling mechanism | Thread concept | Concurrent parallel | CPU | system call | Task switching |
- File system > > Document concept | file system | Index node | Mount directory | Root file system | VFS | File handle | Pipeline file |
- Hardware Architecture > > Compilation basis | Assembly parameters | Working mode | register | Abnormal takeover | Compilation summary | Interrupt switching | Interrupt concept | Interrupt management |
- Device driver > > Character device | Console | Remote login |
Million note source code | buckle details everywhere
-
The purpose of annotating the core of millions of Chinese characters is to see its capillaries and cell structure clearly, which is equivalent to looking at the core with a magnifying glass. The kernel is not mysterious. It is easy to be addicted to find answers in the source code with questions. You will find that many articles interpret some problems incorrectly, or it is difficult to justify them if they are not profound. You will slowly form your own new interpretation, and the new interpretation will encounter new problems. You will advance layer by layer and roll forward. You are unwilling to let go with a magnifying glass.
-
< gitee | github | coding | codechina > The four big yards push the official source code, and the official account can be easily read back to millions.