OpenHarmony轻量系统服务管理|进程间通信的核心机制详解(六)

往期知识点记录:

函数实现详解

IPC消息处理函数
//IPC消息处理函数,需要对用户鉴权,从request和response中解析内容
static void HandleIpc(const Request *request, const Response *response)
{
    //获取ipcMsg
    void *ipcMsg = (void *)request->data;
    //获取endpoint
    Endpoint *endpoint = (Endpoint *)response->data;
    //msgId中存储的是下标,根据下标获取router
    Router *router = VECTOR_At(&endpoint->routers, request->msgId);
    if (ipcMsg == NULL)
    {
        return;
    }
    //参数检查
    if (router == NULL || router->proxy == NULL || router->proxy->Invoke == NULL)
    {
        //回收资源
        FreeBuffer(endpoint->context, ipcMsg);
        HILOG_ERROR(HILOG_MODULE_SAMGR, "Invalid IPC router<%p>!", router);
        return;
    }
    //获取ipcMsg中的uid
    uid_t uid = GetCallingUid(ipcMsg);
    //对当前用户鉴权
    if ((strcmp(router->saName.service, SAMGR_SERVICE) != 0) &&
        !JudgePolicy(uid, (const PolicyTrans *)(router->policy), router->policyNum))
    {
        //条件1:service不属于主endpoint
        //条件2:uid不属于policy中的合法值,即policy中不包含当前的uid
        //此用户无权限,释放资源
        FreeBuffer(endpoint->context, ipcMsg);
        HILOG_ERROR(HILOG_MODULE_SAMGR, "Consumer uid<%d> has no permission to access<%s, %d, %d>!",
                    uid, router->saName.service, router->identity.serviceId, router->identity.featureId);
        return;
    }
    //此用户有权限
    //构建消息
    IpcIo req;
    IpcIoInitFromMsg(&req, ipcMsg);
    IpcIo reply;
    uint8 data[IPC_IO_DATA_MAX];
    IpcIoInit(&reply, data, IPC_IO_DATA_MAX, MAX_OBJECT_NUM);
    //解析服务端收到的IPC消息
    router->proxy->Invoke(router->proxy, request->msgValue, ipcMsg, &req, &reply);
    uint32_t flag = 0;
    //获取ipcMsg的标识
    GetFlag(ipcMsg, &flag);
    if (flag == LITEIPC_FLAG_DEFAULT)
    {
        //发送reply消息
        SendReply(endpoint->context, ipcMsg, &reply);
    }
    else
    {
        //回收资源
        FreeBuffer(endpoint->context, ipcMsg);
    }
}
注册服务和功能的访问地址
/*
    函数功能:注册并获取指定服务和功能的访问地址
    函数参数:@context:当前进程的IPC通信端点
             @saName:标识指定服务和功能
             @saInfo:服务和功能的访问地址
             @policy:服务和功能的访问策略
             @policyNum:访问策略总数
    函数描述:首先构建向主endpoint发送feature资源请求的消息体,包含请求的服务名、功能名和token值。
             然后处理主endpoint发回的响应消息,从响应消息中获取主endpoint为服务和功能生成的访问地址和访问策略。返回给RegisterIdentity的调用者
*/
static int RegisterIdentity(const IpcContext *context, const SaName *saName, SvcIdentity *saInfo,
                            PolicyTrans **policy, uint32 *policyNum)
{
    //定义请求结构
    IpcIo req;
    uint8 data[MAX_DATA_LEN];
    //ipc初始化,将req与data缓冲区关联起来
    IpcIoInit(&req, data, MAX_DATA_LEN, 0);
    //将消息放入共享内存,内容为RES_FEATURE,资源类型feature
    IpcIoPushUint32(&req, RES_FEATURE);
    //将消息放入共享内存,内容为OP_PUT操作
    IpcIoPushUint32(&req, OP_PUT);
    //将字符串消息放入共享内存,首先存储字符串的长度,再拷贝字符串的内容。内容为saName->service
    IpcIoPushString(&req, saName->service);
    //将消息放入共享内存,标识feature是否为NULL
    IpcIoPushBool(&req, saName->feature == NULL);
    if (saName->feature != NULL)
    {
        //feature不为NULL,则将feature放入共享内存,先存储feature的长度,再拷贝feature的内容
        IpcIoPushString(&req, saName->feature);
    }
    //将router在routers中的下标token值放入共享内存中
    IpcIoPushUint32(&req, saInfo->token);
    //定义响应消息结构
    IpcIo reply;
    void *replyBuf = NULL;
    //设置主endpoint的地址
    SvcIdentity samgr = {SAMGR_HANDLE, SAMGR_TOKEN, SAMGR_COOKIE};
    //LITEIPC_FLAG_DEFAULT表示发送和响应
    //LITEIPC_FLAG_ONEWAY表示只发送
    //向主endpoint发送注册feature的请求消息,数据在req中,响应由reply和replyBuf接收。Transact即SendRequest函数
    int ret = Transact(context, samgr, INVALID_INDEX, &req, &reply, LITEIPC_FLAG_DEFAULT, (uintptr_t *)&replyBuf);
    ret = -ret;
    if (ret == LITEIPC_OK)
    {
        //从reply中读取消息
        ret = IpcIoPopInt32(&reply);
    }
    if (ret == EC_SUCCESS)
    {
        //从reply中读取主endpoint返回给当前进程的Svcindentity,更新saInfo的信息
        saInfo = IpcIoPopSvc(&reply);
        //从reply中获取该服务下指定功能的访问策略信息
        GetRemotePolicy(&reply, policy, policyNum);
    }
    if (replyBuf != NULL)
    {
        //回收资源
        FreeBuffer(context, replyBuf);
    }
    return ret;
}
注册feature
//注册Feature,返回注册失败的个数
static int RegisterRemoteFeatures(Endpoint *endpoint)
{
    int nums = 0;
    //获取router个数
    int size = VECTOR_Size(&endpoint->routers);
    int i;
    SvcIdentity identity;
    //遍历router
    for (i = 0; i < size; ++i)
    {
        //根据下标返回指定router
        Router *router = VECTOR_At(&endpoint->routers, i);
        if (router == NULL)
        {
            continue;
        }
        identity.handle = endpoint->identity.handle;
        //token值即router在routers集合中的下标
        identity.token = i;
        //注册并获取指定服务和功能的访问地址
        int ret = RegisterIdentity(endpoint->context, &(router->saName), &identity, &(router->policy),
                                   &(router->policyNum));
        if (ret == EC_SUCCESS)
        {
            //注册成功
            ++nums;
        }
        HILOG_DEBUG(HILOG_MODULE_SAMGR, "RegisterRemoteFeatures<%s, %s> ret:%d",
                    router->saName.service, router->saName.feature, ret);
    }
    //返回注册失败的个数
    return VECTOR_Num(&endpoint->routers) - nums;
}
注册endpoint
/*
    函数功能:向主Endpoint注册当前的Endpoint信息
    函数参数:@context:IPC通信的上下文
             @identity:本端进程的地址标识
    函数返回:注册成功 返回EC_SUCCESS,注册失败 返回EC_FAILURE
    函数描述:向主endpoint注册当前进程的endpoint信息,通过共享内存的方式,发送注册endpoint的请求。
             若注册成功,主endpoint会发送响应给当前进程的endpoint,响应消息包含了handle。该handle作为当前进程通信地址identity的一部分。
             若注册失败,会重试MAX_RETRY_TIMES次数,等待主endpoint就绪。多次重试均失败则返回EC_FAILURE。
*/
static int RegisterRemoteEndpoint(const IpcContext *context, SvcIdentity *identity)
{
    //定义请求消息结构
    IpcIo req;
    //数据段
    uint8 data[MAX_DATA_LEN];
    //初始化请求消息
    IpcIoInit(&req, data, MAX_DATA_LEN, 0);
    //将消息放入共享内存,内容为RES_ENDPOINT,即当前消息为注册endpoint的请求
    IpcIoPushUint32(&req, RES_ENDPOINT);
    //消息类型OP_POST
    IpcIoPushUint32(&req, OP_POST);
    uint8 retry = 0;
    //若主endpoint未就绪,最大重试次数为MAX_RETRY_TIMES
    while (retry < MAX_RETRY_TIMES)
    { 
        ++retry;
        //定义响应消息结构
        IpcIo reply;
        //响应消息的缓冲区
        void *replyBuf = NULL;
        //设置主Endpoint的标识信息
        SvcIdentity samgr = {SAMGR_HANDLE, SAMGR_TOKEN, SAMGR_COOKIE};
        //发送消息
        //LITEIPC_FLAG_DEFAULT表示发送和响应
        //LITEIPC_FLAG_ONEWAY表示只发送
        //向主endpoint发送注册endpoint的请求消息,数据在req中,响应由reply和replyBuf接收。Transact即SendRequest函数
        int err = Transact(context, samgr, INVALID_INDEX, &req, &reply, LITEIPC_FLAG_DEFAULT, (uintptr_t *)&replyBuf);
        if (err == LITEIPC_OK)
        {//消息发送完毕
            //获取主Endpoint返回的handle
            identity->handle = IpcIoPopUint32(&reply);
            if (replyBuf != NULL)
            {
                //释放内存
                FreeBuffer(context, replyBuf);
            }
            if (identity->handle == (uint32)INVALID_INDEX)
            {
                //返回的handle无效,即主Endpoint未就绪,重试
                continue;
            }
            return EC_SUCCESS;
        }
        //睡眠间隔时间
        sleep(RETRY_INTERVAL);
    }
    return EC_FAILURE;
}

写在最后

如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙

  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请看下图提示:
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值