1、什么是IPC

在做一个比较简单的项目时,我们可以使用全局变量等作为标志位进行逻辑判断,但是在功能较多的项目上时,使用全局变量作为程序间的标志位当然是不可行的,代码将会混乱且复杂,不利于解耦,因此需要使用到IPC(Interprocess communication),IPC是模块间的通信组件,主要实现的是任务之间的消息转发,广播等功能,模块功能和实现都较为简单。

2、需求分析

  • 模块间的消息传递主要是一对多消息和一对一消息,所有的消息都是先经过IPC模块再进行分发,按照模块的消息发送条件判断消息最终到达的目的地
  • 消息从发送模块到接收模块要求比较高的执行效率。

3、架构设计

  • 使用IPC模块的其他业务模块通过IPC模块实现间接通信,解除模块之间的耦合。
  • IPC的通信分为同步处理和异步处理

    • 使用同步处理时,接收方模块的业务可能会消耗发送方模块的时间长度
    • IPC的异步处理可能会出现消息没来及消费的问题,如果阻塞则会影响生产者消息传递,并且有消息丢失的可能性。阻塞时需要短或者不阻塞,为了保证消息不丢失需要在消息队列溢出时把溢出的消息及时消费。
  • IPC模块与其他模块的交互就是消息的接收和消息的转发,消息的接收可以是一个统一的接口给其他模块调用,消息的分发需要保存一张消息接收的表,表头包含模块的订阅判断函数指针。
  • 应用场景

    • 同步单播

    IPC1.png

    • 同步广播

    IPC2.png

    • 异步单播

    IPC3.png

    • 异步广播

    IPC4.png

4、代码实现

IPC.C

#include "ipc.h"
static const IPC_MODULE_Typedef *IPC_T;
uint16_t IPC_ModNum = 0;
/**
 * @brief IPC模块初始化函数 
 * @param ipc_t 消息接收模块表
 * @param IPC_ModuleNum 消息接收模块数量
 */
void IPC_ModInit(const IPC_MODULE_Typedef *ipc_t,uint16_t IPC_ModuleNum)
{
    IPC_T = ipc_t;
    IPC_ModNum = IPC_ModuleNum;
}
/**
 * @brief IPC消息发送
 * @param RecvModId  目标模块ID,为0xFF时为所有模块
 * @param MsgId 消息ID
 * @param MsgParam 消息参数
 */
void IPC_SendMsg(uint32_t RecvModId,uint32_t MsgId,uint32_t *MsgParam ,uint32_t Msglen)
{
    int i = 0;
    if (ID_MODULE_ALL == RecvModId) //所有模块都接收的消息
    {
        for (i; i < IPC_ModNum; i++)
        {
            IPC_T[i].eventHandleFunc(MsgId, MsgParam,Msglen);
        }
    }
    else //指定模块接收的消息
    {
        for (i; i < IPC_ModNum; i++)
        {
            if (RecvModId == IPC_T[i].mod_Id)
            {
                IPC_T[i].eventHandleFunc(MsgId, MsgParam,Msglen);
            }
        }
    }
}

IPC.h

#ifndef IPC_H
#define IPC_H
#include "types.h"
#define ID_MODULE_ALL 0XFF

typedef void (*EventHandleFunc)(uint32_t MsgId,uint32_t MsgParam,uint32_t Msglen);
/*IPC模块抽象类*/
typedef struct IPC_MODUILE_Typedef_t
{
    uint16_t mod_Id;
    EventHandleFunc eventHandleFunc;
}IPC_MODULE_Typedef;
/*IPC消息*/
typedef struct IPC_Typedef_t
{
    uint32_t msgId;
    uint32_t msgParam;
    uint32_t msgLen;
}IPC_Typedef;

/*IPC消息*/
/**
 * @brief IPC模块初始化函数 
 * @param ipc_t 消息接收模块表
 * @param IPC_ModuleNum 消息接收模块数量
 */
void IPC_ModInit(const IPC_MODULE_Typedef *ipc_t,uint16_t IPC_ModuleNum);
/**
 * @brief IPC消息发送
 * @param RecvModId  目标模块ID,为0xFF时为所有模块
 * @param MsgId 消息ID
 * @param MsgParam 消息参数
 * @param Msglen 消息长度
 */
void IPC_SendMsg(uint32_t RecvModId,uint32_t MsgId,uint32_t *MsgParam ,uint32_t Msglen);
#endif

5、使用案例

#include "msg.h"
#include "ipc.h"

extern void mod_1_event_handle(uint32_t msg_id,uint32_t *msg_param,uint32_t len);
extern void mod_2_event_handle(uint32_t msg_id,uint32_t *msg_param,uint32_t len);
extern void mod_3_event_handle(uint32_t msg_id,uint32_t *msg_param,uint32_t len);


const IPC_MODULE_Typedef ipc_table[MOD_ID_BUTT] = 
{
    {MOD_ID_1,mod_1_event_handle},
    {MOD_ID_2,mod_2_event_handle},
    {MOD_ID_3,mod_3_event_handle},
};
void mod_ipc_init(void)
{
    IPC_ModInit(ipc_table,MOD_ID_BUTT);
}

如果需要给模块2发送一个事件

IPC_SendMsg(MOD_ID_2,POWER_OFF,NULL,0);

模块2的事件处理(同步方式)

void mod_2_event_handle(uint32_t msg_id,uint32_t *msg_param,uint32_t len)
{
    switch(msg_id)
    {
        case POWER_OFF:
            //关机
        break;
        default:
        break;
    }
}
文章目录