SA服务的配置和启动流程 原创
OpenHarmony中的SA(System Ability)服务是指系统能力服务,是OpenHarmony操作系统中提供系统级功能的服务。这些服务是系统运行的基础,为应用程序和其他系统组件提供了一系列核心功能和接口。
SA服务包含在系统服务管理框架子系统,负责提供各种系统级服务。
SystemAbility实现一般采用libXXX.z.so+XXX.cfg + profile.json 的方式由init进程执行对应的XXX.cfg文件拉起相关SystemAbility进程。
一、SA服务进程编写
1、代码结构
主要分为以下四个类
- ICastEngineDlnaService:虚类,定义该服务对外提供的能力集合函数,统一继承IPC接口类IRemoteBroker;同时实现该IPC对外接口唯一标识符DECLARE_INTERFACE_DESCRIPTOR(XXX);该标识符用于IPC通信的校验等目的;
/foundation/CastEngine/castengine_dlna/dmr/service/include/i_cast_engine_dlna_service.h
#include "iremote_broker.h"
class ICastEngineDlnaService : public IRemoteBroker {
public:
DECLARE_INTERFACE_DESCRIPTOR(u"DLNA.Ability");
......
}
校验过程:
proxy(客户端):
dataParcel.WriteInterfaceToken(GetDescriptor()) // GetDescriptor()是与 DECLARE_INTERFACE_DESCRIPTOR 宏紧密相关的。这个宏用于声明接 口描述符,而 GetDescriptor() 方法则用于获取这个描述符。
stub(服务端):
data.ReadInterfaceToken(); // 读取客户端写入的描述符,并判断是否为当前服务对应的IPC唯一描述符
- CastEngineDlnaServiceProxy:定义客户端通信代码;
/foundation/CastEngine/castengine_dlna/dmr/service/include/cast_engine_dlna_service_proxy.h
#include "iremote_proxy.h"
class CastEngineDlnaServiceProxy : public IRemoteProxy<ICastEngineDlnaService> {
......
}
- CastEngineDlnaServiceStub:定义服务端通信代码;
/foundation/CastEngine/castengine_dlna/dmr/service/include/cast_engine_dlna_service_stub.h
#include "iremote_stub.h"
class CastEngineDlnaServiceStub : public IRemoteStub<ICastEngineDlnaService>
{
public:
virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override;
......
};
- CastEngineDlnaService:服务实现类;
/foundation/CastEngine/castengine_dlna/dmr/service/include/cast_engine_dlna_service.h
#include "cast_engine_dlna_service_stub.h"
class CastEngineDlnaService : public SystemAbility , public CastEngineDlnaServiceStub {
// 声明系统服务
DECLARE_SYSTEM_ABILITY(CastEngineDlnaService);
......
}
/foundation/CastEngine/castengine_dlna/dmr/service/src/cast_engine_dlna_service.cpp
#include "cast_engine_dlna_service.h"
// 注册系统服务,将CastEngineDlnaService类注册成为serviceId为1603的systemability
REGISTER_SYSTEM_ABILITY_BY_ID(CastEngineDlnaService, 1603, true);
void CastEngineDlnaService::OnStart() {
// 在这里编写服务启动时的逻辑
}
void CastEngineDlnaService::OnStop() {
// 在这里编写服务停止时的逻辑
}
DECLARE_SYSTEM_ABILITY
通常在系统能力的实现类中使用,而REGISTER_SYSTEM_ABILITY_BY_ID
通常在系统能力的启动入口点使用。
2、系统中的文件路径
system/lib/libcast_engine_dmr.z.so
二、cfg文件
cfg配置文件为linux提供的native进程拉起策略,开机启动阶段由init进程解析配置的cfg文件进行拉起。
1、代码结构
/foundation/CastEngine/castengine_dlna/dmr/etc/init/cast_engine_dmr.cfg
{
"jobs" : [{
# 服务进程名,必填
"name" : "services:cast_engine_dmr",
# 准备服务运行时所需环境,必填
"cmds" : [
"mkdir /data/service/el1/public/cast_engine_dmr 0700 cast_engine_dmr cast_engine_dmr"
]
}
],
"services" : [{
# 服务名,必填
"name" : "cast_engine_dmr",
# 服务的可执行文件全路径和参数,必填
"path" : ["/system/bin/sa_main", "/system/profile/cast_engine_dmr.json"],
# 服务进程的用户ID,必填
"uid" : "cast_engine_dmr",
# 服务进程的组ID,必填
"gid" : ["cast_engine_dmr", "shell"],
# 服务具备权限,选填
"permission" : [
"ohos.permission.DISTRIBUTED_DATASYNC",
"ohos.permission.ACCESS_SERVICE_DM",
"ohos.permission.MICROPHONE",
"ohos.permission.GET_BUNDLE_INFO_PRIVILEGED",
"ohos.permission.GET_NETWORK_STATS",
"ohos.permission.INTERNET"
],
"jobs" : {
# 服务启动时,执行jobs的内容,包括创建文件和设置文件权限
"on-start" : "services:cast_engine_dmr"
},
# 服务的SELinux标签,s0为最低级安全级别,必填
"secon" : "u:r:cast_engine_dmr:s0"
}
]
}
jobs
部分定义了一个作业services:cast_engine_dmr
,该作业在服务启动前执行,创建了一个目录,并设置了权限和所有者。services
部分定义了一个名为cast_engine_dmr
的服务,指定了服务的可执行文件路径、用户ID、组ID、所需权限、启动时要运行的作业,以及服务的SELinux安全上下文。
2、系统中的文件路径
system/etc/init/cast_engine_dmr.cfg
三、JSON配置文件
以c++实现的SA必须配置相关System Ability的profile配置文件才会完成SA的加载注册逻辑,否则没有编写profile配置的System Ability不会完成注册。
1、代码结构
/foundation/CastEngine/castengine_dlna/dmr/sa_profile/1603.json;其中1603为serviceId
{
"process": "cast_engine_dmr", // 进程名,必填
"systemability": [
{
"name": 1603, // serviceId,必须与代码中注册的serviceId保持一致,必填
"libpath": "libcast_engine_dmr.z.so", // 对应服务进程代码生成的动态库文件,必填
"run-on-create": true, // true:进程启动,sa服务注册;false:sa服务在其他模块首次访问该服务时才会启动;必填
"distributed": false, // true:支持跨设备访问;false:仅支持本地跨IPC通信
"dump_level": 1 // 表示systemdumper支持的level等级。默认为1
}
]
}
2、系统中的文件路径
在编译时期,会把各个子系统的JSON文件提取出来,形成一个新的JSON,该JSON文件的value就是对应各个子系统的JSON文件目录。
system/profile/cast_engine_dmr.json
四、配置服务进程的DAC权限
DAC(Discretionary Access Control,自主访问控制)是一种访问控制机制,它允许资源的所有者决定谁可以访问他们的资源以及如何访问。
1、代码结构
passwd文件:1603对应sa配置json文件
/base/startup/init/services/etc/passwd
cast_engine_dmr:x:1603:1603:::/bin/false
这是一个名为 cast_engine_dmr
的系统用户账户,其UID和GID都是1603。
各个字段含义:
-
cast_engine_dmr
:用户名,这是账户的唯一标识符。 -
x
:密码字段。在较新的Linux系统中,实际的密码不存储在/etc/passwd
文件中,而是存储在/etc/shadow
文件中,这个字段只是用来表示密码存在。 -
1603
:用户ID(UID),这是一个数字,用于在系统内部标识用户。UID用于文件权限检查和进程所有权。 -
1603
:组ID(GID),这是用户的主组ID。每个用户都有一个主组,并且可以属于多个辅助组。 -
::
:这个字段通常包含用户的全名或者描述信息,这里为空,表示没有提供。 -
/bin/false
:shell是linux的命令解释器,用户和Linux内核之间沟通的桥梁shell命令解释器用于将用户输入的命令转换成系统可识别的机器语言通常情况下,Linux系统默认使用的命令解释器是/bin/bash
group文件:1603对应sa配置json文件
/base/startup/init/services/etc/group
cast_engine_dmr:x:1603:
2、系统中的文件路径
system/etc/passwd
system/etc/group
五、SA服务启动流程
1、系统开机启动流程
系统上电加载内核后,按照以下流程完成系统各个服务和应用的启动:
-
内核加载init进程:一般在bootloader启动内核时通过设置内核的cmdline来指定init的位置。
-
基础环境初始化:init进程启动后,会挂载tmpfs,procfs,创建基本的dev设备节点,提供最基本的根文件系统。
tmpfs:这是一个内存上的文件系统,用于存储临时数据,比如在启动期间创建的目录、缓存等。它不会持久化到磁盘,当系统重启时会自动清除。
procfs:这个文件系统提供内核运行时信息的接口,如进程列表、系统配置、硬件状态等。init进程通常会挂载procfs,以便在启动早期获取和管理这些信息。
-
热插拔事件监听:init也会启动ueventd监听内核热插拔设备事件,为这些设备创建dev设备节点。包括block设备各个分区设备都是通过此事件创建。
-
服务启动:挂载完分区后,init 会扫描
/system/etc/init
目录下的init.cfg
文件,根据配置启动各个系统服务,包括 SA(Service Ability,能力服务)。 -
服务注册与管理:samgr是各个SA的服务注册中心,每个SA启动时,都需要向samgr注册,每个SA会分配一个serviceId,应用可以通过该serviceId访问SA。
-
用户程序管理:foundation是一个特殊的SA服务进程,提供了用户程序管理框架及基础服务。由该进程负责应用的生命周期管理。由于应用都需要加载JS的运行环境,涉及大量准备工作,因此appspawn作为应用的孵化器,在接收到foundation里的应用启动请求时,可以直接孵化出应用进程,减少应用启动时间。
扩展了解一下,init进程不同阶段
2、SA服务启动流程
1、初始化服务
启动SA服务是通过导入JSON配置文件执行system/bin/sa_main程序启动服务的。sa_main程序会根据JSON配置文件初始化所有SA服务配置,包括serviceId,服务进程名等。
2、启动服务
sa_main程序会解析进程名称并打开和加载服务对应的so文件;等待SystemAbilityManager启动(提供SA服务的注册和查找,实现Binder机制等重要功能);启动服务;
3、注册服务
在加载so文件时会加载服务的类这时会调用到REGISTER_SYSTEM_ABILITY_BY_ID
函数,会识别到将服务的JSON信息加入到了abilityMap_,在samgr服务中解析并存入该系统服务相关信息;
4、服务间IPC通信
服务端进程通过Binder驱动在Service Manager中注册自己的服务;客户端进程通过Binder驱动向Service Manager查询并获取服务端的代理对象;客户端通过代理对象发起远程调用,Binder驱动负责将调用请求和参数传递给服务端,并将结果返回给客户端。
扩展了解一下,binder机制
赞赞赞