OpenHarmony开发者论坛

标题: 【SUBJECT技术】OpenHarmony IPC 之ServiceAbility应用(系统架构) [打印本页]

作者: 诚迈_雨哥    时间: 2024-6-1 16:29
标题: 【SUBJECT技术】OpenHarmony IPC 之ServiceAbility应用(系统架构)
[md]# 一、重要性

OpenHarmony IPC 之ServiceAbility应用极其广泛。例如在OpenHarmony以下四个目录共找到约1087个子系统用到了这种机制,其重要性可见一斑。
`grep -nr "::OnRemoteRequest" ./foundation/ ./base/ ./drivers/ ./kernel/`
调试时OnRemoteRequest()函数是必经之路,服务端函数有没有调起就在这里“打桩”或输出日志。

Client-Server软件模型外加Interface接口共同组成了OpenHarmony诸多子系统的架构,使其系统的稳定性、扩展性以及进程通信能力得到加强。子系统中均可见到Interface接口目录、Framework客户端目录以及Service服务端目录,工作重点一般集中在Service服务端,如下图所示。

![clientserver2.PNG](https://forums-obs.openharmony.c ... berp47hckptt5tt.png "clientserver2.PNG")

![](./figures/OpenharmonyArch_clientserver.PNG)

这种软件架构提供了丰富的软件接口,又提供了灵活的接口组织策略,很容易将一个子系统与另一个子系统相连接,形成一个有机的整体。典型的通路是Foundation→Base→Drivers(Third_paty)

![interface_proxy_stub.PNG](https://forums-obs.openharmony.c ... jj3qn0zh37x389z.png "interface_proxy_stub.PNG")

![](./figures/interface_proxy_stub.PNG)
传统紧凑的软件架构因扩展性、稳定性饱受诟病,与其形成了鲜明对比,如下图:

![interface_implement.PNG](https://forums-obs.openharmony.c ... qqilizgdhz07rtr.png "interface_implement.PNG")



![](./figures/interface_implement.PNG)
**说明**
以下部分内容来自参考链接,他们已经整理的很好了,此处是借花献佛!

# 二、IPC 介绍

## IPC(Inter-Process Communication)

IPC是用于进程间通信的技术,指的是进程间的数据交互过程。它包括各种形式的消息传递,共享资源,以及同步对象,如互斥量等,以确保安全的并发访问共享资源。IPC通常使用Binder驱动,主要用于设备内的跨进程通信,如OpenHarmony系统中的进程间通信。

IPC与RPC(Remote Procedure Call)机制用于实现跨进程通信,不同的是前者使用Binder驱动,用于设备内的跨进程通信,而后者使用软总线驱动,用于跨设备跨进程通信。IPC和RPC通常采用客户端-服务器(Client-Server)模型,服务请求方(Client)可获取提供服务提供方(Server)的代理 (Proxy),并通过此代理读写数据来实现进程间的数据通信。通常,系统能力(System Ability)Server侧会先注册到系统能力管理者(System Ability Manager,缩写SAMgr)中,SAMgr负责管理这些SA并向Client提供相关的接口。Client要和某个具体的SA通信,必须先从SAMgr中获取该SA的代理,然后使用代理和SA通信。三方应用可以使用FA提供的接口绑定服务提供方的Ability,获取代理,进行通信。下文使用Proxy表示服务请求方,Stub表示服务提供方。

总结来说,IPC是进程间通信的方式,侧重于进程内的数据共享和资源管理。IPC通信机制架构图如下:

![aa.PNG](https://forums-obs.openharmony.c ... l5959rvoxzyhhfa.png "aa.PNG")

## IPC代码目录

![b.png](https://forums-obs.openharmony.c ... y1do4u90yrry199.png "b.png")

![](./figures/b.png)

## 约束

* 单个设备上跨进程通信时,传输的数据量最大约为1MB,过大的数据量请使用匿名共享内存。
* 不支持在RPC中订阅匿名Stub对象(没有向SAMgr注册Stub对象)的死亡通知。
* 不支持把跨设备的Proxy对象传递回该Proxy对象所指向的Stub对象所在的设备,即指向远端设备Stub的Proxy对象不能在本设备内进行二次跨进程传递。

# 三、ServiceAbility应用

## 编译依赖

sdk依赖:

```
external_deps = [
  "ipc:ipc_core",
]
```

此外, IPC/RPC依赖的refbase实现在公共基础库下,请增加对utils的依赖:

```
external_deps = [
  "c_utils:utils",
]
```

**说明**
通过SA的标识和设备NetworkId,从SAMgr获取Proxy,通过Proxy实现与Stub的跨进程通信。

## 实现跨进程通信的基本步骤:

1. 定义接口类
   接口类继承IRemoteBroker,定义描述符、业务函数和消息码。
2. 实现服务提供端(Stub)
   Stub继承IRemoteStub(Native),除了接口类中未实现方法外,还需要实现AsObject方法及OnRemoteRequest方法。
3. 实现服务请求端(Proxy)
   Proxy继承IRemoteProxy(Native),封装业务函数,调用SendRequest将请求发送到Stub。
4. 注册SA
   服务提供方所在进程启动后,申请SA的唯一标识,将Stub注册到SAMgr。
5. 获取SA
   通过SA的标识和设备NetworkId,从SAMgr获取Proxy,通过Proxy实现与Stub的跨进程通信。

## IPC接口说明

| 类/接口       | 方法                                                                                                         | 功能说明                                                                        |
| ------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- |
| IRemoteBroker | sptr<IRemoteObject> AsObject()                                                                               | 返回通信对象。Stub端返回RemoteObject对象本身,Proxy端返回代理对象。             |
| IRemoteStub   | virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) | 请求处理方法,派生类需要重写该方法用来处理Proxy的请求并返回结果。               |
| IRemoteProxy  | Remote()->SendRequest(code, data, reply, option)                                                             | 消息发送方法,业务的Proxy类需要从IRemoteProxy类派生,该方法用来向对端发送消息。 |

## 使用说明

### 1. 定义IPC接口ITestAbility

IPC接口继承IPC基类接口IRemoteBroker,接口里定义描述符、业务函数和消息码,其中业务函数在Proxy端和Stub端都需要实现。

```
class ITestAbility : public IRemoteBroker {
public:
    // DECLARE_INTERFACE_DESCRIPTOR是必须的, 入参需使用std::u16string;
    DECLARE_INTERFACE_DESCRIPTOR(u"test.ITestAbility"); // DESCRIPTOR接口描述符建议使用"组件名.类名"的格式
    int TRANS_ID_PING_ABILITY = 1; // 定义消息码
    virtual int TestPingAbility(const std::u16string &dummy) = 0; // 定义业务函数
};
```

### 2. 定义和实现服务端TestAbilityStub

该类是和IPC框架相关的实现,需要继承自IRemoteStub<ITestAbility>。Stub端作为接收请求的一端,需重写OnRemoteRequest方法用于接收客户端调用。

```
class TestAbilityStub : public IRemoteStub<ITestAbility> {
public:
    virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override;
    int TestPingAbility(const std::u16string &dummy) override;
};

int TestServiceStub::OnRemoteRequest(uint32_t code,
    MessageParcel &data, MessageParcel &reply, MessageOption &option)
{
    if (data.ReadInterfaceToken() != GetDescriptor()) { //校验是否为本服务的接口描述符,避免中继攻击
        return -1;
    }
    switch (code) {
        case TRANS_ID_PING_ABILITY: {
            std::u16string dummy = data.ReadString16();
            int result = TestPingAbility(dummy);
            reply.WriteInt32(result);
            return 0;
        }
        default:
            return IPCObjectStub::OnRemoteRequest(code, data, reply, option);
    }
}
```

### 3. 定义服务端业务函数具体实现类TestAbility

```
class TestAbility : public TestAbilityStub {
public:
    int TestPingAbility(const std::u16string &dummy);
}

int TestAbility::TestPingAbility(const std::u16string &dummy) {
    return 0;
}
```

### 4. 定义和实现客户端TestAbilityProxy

该类是Proxy端实现,继承自IRemoteProxy<ITestAbility>,调用SendRequest接口向Stub端发送请求,对外暴露服务端提供的能力。

```
class TestAbilityProxy : public IRemoteProxy<ITestAbility> {
public:
    explicit TestAbilityProxy(const sptr<IRemoteObject> &impl);
    int TestPingService(const std::u16string &dummy) override;
private:
    static inline BrokerDelegator<TestAbilityProxy> delegator_; // 方便使用iface_cast宏
}

TestAbilityProxy::TestAbilityProxy(const sptr<IRemoteObject> &impl)
    : IRemoteProxy<ITestAbility>(impl)
{
}

int TestAbilityProxy::TestPingService(const std::u16string &dummy) {
    MessageOption option;
    MessageParcel dataParcel, replyParcel;
    if(!dataParcel.WriteInterfaceToken(GetDescriptor())) { //所有对外接口的proxy实现都要写入接口描述符,用于stub端检验
        return -1;
    }
    if(!dataParcel.WriteString16(dummy)) {
        return -1;
    }
    int error = Remote()->SendRequest(TRANS_ID_PING_ABILITY, dataParcel, replyParcel, option);
    int result = (error == ERR_NONE) ? replyParcel.ReadInt32() : -1;
    return result;
}
```

### 5. 同步调用与异步调用

MessageOption作为发送接口(原型如下)的入参,可设定同步(TF_SYNC)、异步(TF_ASYNC),默认情况下设定为同步,其余可通过MessageOption构造方法或void SetFlags(int flags)设定。

```
    int SendRequest(uint32_t code, MessageParcel &data,
        MessageParcel &reply, MessageOption &option) override;
    MessageOption option;
    option.setFlags(option.TF_ASYNC);
```

### 6. SA注册与启动

SA需要将自己的TestAbilityStub实例通过AddSystemAbility接口注册到SystemAbilityManager,设备内与分布式的注册参数不同。

```
// 注册到本设备内
auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
samgr->AddSystemAbility(said, new TestAbility());

// 在组网场景下,会被同步到其他设备上
auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
ISystemAbilityManager::SAExtraProp saExtra;
saExtra.isDistributed = true; // 设置为分布式SA
int result = samgr->AddSystemAbility(said, new TestAbility(), saExtra);
```

### 7. SA获取与调用

通过SystemAbilityManager的GetSystemAbility方法可获取到对应SA的代理IRemoteObject,然后构造TestAbilityProxy即可。

```
// 获取本设备内注册的SA的proxy
sptr<ISystemAbilityManager> samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
sptr<IRemoteObject> remoteObject = samgr->GetSystemAbility(said);
sptr<ITestAbility> testAbility = iface_cast<ITestAbility>(remoteObject); // 使用iface_cast宏转换成具体类型

// 获取其他设备注册的SA的Proxy
sptr<ISystemAbilityManager> samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
sptr<IRemoteObject> remoteObject = samgr->GetSystemAbility(sdid, deviceId); // deviceId是指定设备的标识符
sptr<TestAbilityProxy> proxy(new TestAbilityProxy(remoteObject)); // 直接构造具体Proxy
```

# 参考链接

* Openharmony IPC通信(L2) : https://www.51cto.com/article/701821.html
* 鸿蒙Service Ability的前世今生--基础篇:https://blog.csdn.net/procedurecode/article/details/130222081
* HarmonyOS4.0——IPC与RPC通信: https://zhuanlan.zhihu.com/p/681121447
[/md]




欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/) Powered by Discuz! X3.5