OHOS标准系统的SAMGR代码解读(v3.1)--1--safwk 原创 精华
::: hljs-center
OHOS标准系统的SAMGR代码解读(v3.1)–1–safwk
梁开祝 20220904
:::
OHOS标准系统的safwk组件位于3.1分支代码的//foundation/distributedschedule/safwk/目录下,在最新的master分支代码则是//foundation/systemabilitymgr/safwk/目录下。
通过tree命令将目录树结构打印出来,把测试相关代码以及我们暂不关心的文件去掉后,结果如下图所示:
可以看到通过safwk组件中的三个BUILD.gn文件,把safwk组件的配置文件和源代码分别编译成以下3个目标:
- 配置文件 profile
- 可执行程序sa_main
- 动态链接库文件 system_ability_fwk(即 libsystem_ability_fwk.z.so)
下面分别来看一下。
1. 配置文件 profile
在3.1 release分支代码的 safwk/etc/profile/目录下,只有BUILD.gn和foundation_trust.xml两个文件(foundation.cfg 文件实际在//foundation/appexecfwk/standard/sa_profile/目录下)。
在master分支代码的 safwk/etc/profile/目录下,除了BUILD.gn、foundation_trust.xml和foundation.cfg外,还新增了一个foundation_permission_desc.json文件(BUILD.gn中未见对该新增文件的使用)。
可见这些配置文件在3.1 release和master分支上有细微差别,但不影响我们对safwk组件的分析。
1.1 foundation.cfg
这是标准系统的foundation进程的启动配置文件,在标准系统的开机启动阶段,由init进程解析 /system/etc/init/foundation.cfg文件,并执行其中的命令拉起foundation进程(实际上在标准系统中有非常多的进程以类似的方式拉起进程,可搜索并过滤/system/etc/init/*.cfg文件进行确认)。
查看/system/etc/init/foundation.cfg文件:
......(略)
"services" : [{
"name" : "foundation",
"path" : ["/system/bin/sa_main", "/system/profile/foundation.xml"],
......
"jobs" : {
"on-start" : "services:foundation"
}
}
]
标准系统的foundation进程没有生成自己的可执行程序,而是借用了/system/bin/sa_main可执行程序创建并初始化foundation进程的运行环境,根据传入的/system/profile/foundation.xml参数,运行foundation.xml文件中指定的各个SA,保证了这组SA能够顺利对系统提供服务。
1.2 foundation.xml
/system/profile/foundation.xml 文件是编译系统自动生成的,它由一组 serviceid.xml 合并而成。在foundation.xml中可以看到一组SA的ID,如401、3501、4802等,我们可以在系统代码根目录下分别搜索401.xml、3501.xml、4802.xml等,可以找到对应的文件。在这些文件中都可以看到一个“<process>foundation</process>”字段,表明这些SA都运行在foundation进程空间,而“<systemability>xxx</systemability>”之间的各个字段,则是SA的详细配置信息,在启动SA时会用得到(这些配置信息的详情,请查阅safwk/README_zh.md文档)。
1.3 foundation_trust.xml 和 foundation_permission_desc.json
foundation_trust.xml记录了可以在foundation进程中加载和运行的SA列表,见4.1节的分析。
foundation_permission_desc.json目前未见到使用的地方。
1.4 其他进程的.cfg和.xml
其他进程如果也是用sa_main可执行程序来拉起进程,则它们也必须要有对应的.cfg和serviceid.xml文件。
比如softbus_server进程,会有dsoftbus/core/frame/standard/init/src/softbus_server.cfg 和 dsoftbus/core/frame/standard/sa_profile/4700.xml。这两个文件经编译系统的处理后,对应生成系统中的/system/etc/init/softbus_server.cfg和/system/profile/softbus_server.xml。(《沉浸式剖析OpenHarmony源代码》的8.7.1节内容详细分析了softbus_server进程的启动流程,本文不再赘述。)
2. 可执行程序sa_main
可执行程序sa_main由 safwk/services/safwk/src/main.cpp 文件单独编译生成。查看其源代码,可以看到它的实际工作很简单:
执行main()函数,在系统中创建并初始化进程的运行环境;然后读取并解析参数传入的.xml文件(如foundation.xml 或softbus_server.xml),将进程的名字设置为.xml文件中“<process>name</process>”字段指定的名字;最后执行:
LocalAbilityManager::GetInstance().DoStartSAProcess(profilePath, saId);
//注意:在系统开机阶段自动启动进程时,saId参数的值此时都是-1,并非传入具体的SA的ID。
以此启动.xml文件中指定的一个或多个SA。
3. 动态链接库文件 system_ability_fwk
system_ability_fwk动态链接库为sa_main启动具体的SA提供支持,它的源代码、依赖关系、类之间的关系等,如下图所示:
3.1 组件的依赖关系
注意该库的依赖关系。其中依赖与samgr相关的3个动态链接库,会在解读samgr组件时进行分析。
依赖的ipc_core组件所提供的IPC功能(对应上图右上角的一组虚基类),本文暂不深入分析。
3.2 类的继承关系
由上图蓝色底色部分中的灰色底色所指示的三个类之间的关系可以看出:
-
LocalAbilityManagerStub 类继承 ILocalAbilityManager 接口类,为启动SA提供服务端(Stub)接口,具体的启动SA的一般流程(或公共流程)由其子类LocalAbilityManager实现。
-
LocalAbilityManager类实现启动SA的一般性流程,并记录了当前进程内的所有SA的详细信息、当前LocalAbilityManager类对象、SA的任务信息等等。
-
每一个SA对应一个SystemAbility类的对象,该对象记录了serviceid.xml文件中的 “<systemability>xxx</systemability>”字段中记录的信息。
-
LocalAbilityManager类是SystemAbility类的友元类,LocalAbilityManager类可以直接访问SystemAbility类的所有成员和函数。
它们的关系如下图所示:
4. 启动SA的一般流程
如上面1.1所述,init进程读取和解析 /system/etc/init/*.cfg 文件,在启动到具体的jobs阶段时,执行对应的命令以启动服务(即SA)。
下面以softbus_server(SA4700)的启动为例,简单说明与safwk组件相关的SA一般性启动流程。
4.1 DoStartSAProcess() 函数
sa_main可执行程序的执行流程如上图所示,它的主要工作都在调用的如下函数的步骤中了。
LocalAbilityManager::GetInstance().DoStartSAProcess(profilePath, saId);
在DoStartSAProcess()函数中的4个步骤,简单说明如下:
步骤[4-1]:调用InitSystemAbilityProfiles()函数
主要是分析传入main()的第2个参数中指定的.xml文件,并将该文件的<systemability>列表中记录的各个SA的信息,逐一分析并提取信息记录到 ParseUtil类的如下两个字段中:
//当前进程的SA链表,链表上的每一个节点都是一个SaProfile结构体
std::list<SaProfile> saProfiles_;
//当前进程的名字,如foundation、softbus_server等
std::u16string procName_;
然后还会确认是否存在“/system/profile/进程名_trust.xml”文件(如1.3中提到的foundation_trust.xml文件)。存在该文件的话,则会通过CheckTrustSa()确认上述SA链表中的SA是否都允许在当前进程中加载和运行,并将不允许在当前进程中加载和运行的SA从SA链表中删除,在接下来的步骤中就不去加载和运行它了。
最后调用OpenSo()将SA链表中的SA指定的动态链接库加载到当前进程的运行空间中。
void ParseUtil::OpenSo()
{
for (auto& saProfile : saProfiles_) {
if (saProfile.runOnCreate) {
OpenSo(saProfile);
}
}
}
加载动态链接库时,会根据不同的SA的类构造函数创建各自的SA服务对象,并执行SystemAbility::MakeAndRegisterAbility(Xxx obj)将SA的服务对象注册到 LocalAbilityManager 类的std::map<int32_t, SystemAbility*> abilityMap_ 成员中备用,该成员是一个map数据结构,key是saId,value对应具体的一个SA对象指针。
步骤[4-2] :调用CheckSystemAbilityManagerReady()函数
这一步主要是通过尝试获取samgrProxy来确认远程的samgr进程的saManager服务是否可以访问。
samgr进程几乎是用户空间上最早运行的系统进程了,所有的SA都需要依赖samgr提供的服务。
步骤[4-3] :调用InitializeSaProfiles()函数
这一步主要是执行InitializeRunOnCreateSaProfiles()函数,将SA链表(saProfiles_)中的SA依次在abilityMap_中提取匹配的SA,并根据SA属性中的<bootphase>字段的配置,将SA加入到 LocalAbilityManager 类的std::map<uint32_t, std::list<SystemAbility*>> abilityPhaseMap_成员中,该成员是一个map数据结构,key是如下枚举中的一个值(表示不同的启动阶段):
enum {
BOOT_START = 1,
CORE_START = 2,
OTHER_START = 3,
};
value则是一个list的链表结构,链表上的每个节点都是一个SA的对象指针。
当系统启动到枚举指定的阶段时,就会依次启动list链表中的所有SA。
bootphase:可不设置;可以设置的值有三种:BootStartPhase、CoreStartPhase、OtherStartPhase(默认类型),三种优先级依次降低,在同一个进程中,会优先拉起注册配置BootStartPhase的SystemAbility,然后是配置了CoreStartPhase的SystemAbility,最后是OtherStartPhase;当高优先级的SystemAbility全部启动注册完毕才会启动下一级的SystemAbility的注册启动。
步骤[4-4] :调用Run()函数
这一步调用Run()函数向saManager注册SA并启动SA对应的任务。
4.2 Run() 函数
LocalAbilityManager::Run() 的流程(见附件大图),按函数调用分为如下5个步骤,简单分析如下。
步骤[5-1]:向远程的samgr注册当前进程的 localAbilityManager_ 对象
这一步首先获取远程samgr的服务代理,通过该代理发送IPC消息给saManager,把当前进程的名字和该进程的 localAbilityManager_ 对象注册到samgr的 std::map<std::u16string, sptr<IRemoteObject>> systemProcessMap_ 成员中,该成员是一个map数据结构,key是进程名字符串,value是该进程的localAbilityManager_ 对象(也是一个IRemoteObject对象)。
步骤[5-2]:启动当前进程的主线程
步骤[5-3]:按启动的阶段(Phase)依次启动abilityPhaseMap_中对应阶段的SA链表中的每一个SA
这一步相当于按SA的优先级别先后启动进程中的每一个SA了。如foundation进程,它有若干个SA,其中一部分SA是在CORE_START阶段先启动的,另一部分SA是在默认的OTHER_START阶段后启动的;而softbus_server进程只有一个SA4700,默认是在OTHER_START阶段启动的。
进程中的每一个SA都会在当前进程中创建一个任务(线程),并绑定任务的入口函数为 LocalAbilityManager::StartSystemAbilityTask()。任务启动后就执行该函数,先确认SA的依赖关系,然后进入启动每一个具体的SA的流程(如上图中的启动软总线SA的流程,这就是每一个SA各自独特的启动流程部分了)。
步骤[5-4]:向远程的samgr注册当前进程的标记为按需启动的SA
run-on-create:true表示进程启动后即向samgr组件注册该SystemAbility;false表示按需启动,即在其他模块访问到该SystemAbility时启动,必配项。
这一步首先获取远程samgr的服务代理,通过该代理发送IPC消息给saManager,把当前进程的名字和按需启动的SA的id注册到samgr的 std::map<int32_t, std::u16string> onDemandAbilityMap_ 成员中,该成员是一个map数据结构,key是按需启动的SA的id,value是该SA所在进程的进程名字。
注意:此时的按需启动的SA还没有启动,即没有执行步骤[5-3]的流程为SA创建任务。
当有其他进程向saManager查询某个按需启动的SA时,saManager会通过pendingTask执行StartOnDemandAbility()函数的流程向SA所在的进程发送IPC消息,要求启动SA,如上图的右下角绿色部分所示。
步骤[5-5]:停止当前进程的主线程
4.3 启动按需启动的SA
saManager向SA所在的进程发送IPC消息要求启动按需启动的SA时,SA所在进程的LocalAbilityManagerStub::OnRemoteRequest()收到IPC消息,并作进一步处理,如下图右半部分所示。
LocalAbilityManagerStub通过其子类的LocalAbilityManager::StartAbility()流程来启动对应的SA。
StartAbility()首先是创建一个任务运行LocalAbilityManager::StartOndemandSystemAbility(),用以加载SA对应的动态链接库,然后执行OnStartAbility()开始启动SA,最终会进入启动具体的SA的流程(即SA自己独特的启动流程)。
5. 小结
以上便是safwk启动一个SA的一般性流程。
safwk组件还提供其他一些功能,如添加、通知、删除SA的监听者,向samgr注销SA等,这就请小伙伴们自行阅读源代码进行理解了。
大佬开始读源码了,必须前排支持
另外这些未来也会在大佬的书里分享吗?
目前定稿的书里并未包含“标准系统的SAMGR代码解读”相关内容
先以博文的形式发布,接受一下小伙伴们的检验,到时候再看看是否编入下一版本的纸质书籍中。