OHOS標準系統的SAMGR代碼解讀(v3.1)--1--Safwk
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)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能夠順利對系統提供服務。
(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文檔)。
(3)foundation_trust.xml 和 foundation_permission_desc.json
foundation_trust.xml記錄了可以在foundation進程中加載和運行的SA列表,見4.1節的分析。
foundation_permission_desc.json目前未見到使用的地方。
(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提供支持,它的源代碼、依賴關系、類之間的關系等,如下圖所示:
(1)組件的依賴關系
注意該庫的依賴關系。其中依賴與samgr相關的3個動態鏈接庫,會在解讀samgr組件時進行分析。
依賴的ipc_core組件所提供的IPC功能(對應上圖右上角的一組虛基類),本文暫不深入分析。
(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一般性啟動流程。
(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對應的任務。
(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]:停止當前進程的主線程
(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等,這就請小伙伴們自行閱讀源代碼進行理解了。
文章相關附件可以點擊下面的原文鏈接前往下載:
https://ost.51cto.com/resource/2283。