概述
Hiview是一個跨平臺的終端設(shè)備維測服務(wù)集,其中是由插件管理平臺和插件實(shí)現(xiàn)的各自功能構(gòu)成整套系統(tǒng)。 本文描述了hiview插件開發(fā)的全部流程。
插件的概念
整節(jié)部分包括了插件的概念,事件源的概念,流水線的概念等基本概念
插件的定義
hiview插件可以理解為運(yùn)行在hiview進(jìn)程中,隨hiview二進(jìn)制一同交付的完成獨(dú)立功能的維測、故障管理模塊。
插件可以是自驅(qū)動的事件源,也可以是由外部事件驅(qū)動的一般插件。其中事件驅(qū)動有兩種方式:流水線驅(qū)動和訂閱查詢驅(qū)動。
單個插件可以靜態(tài)加載(永遠(yuǎn)不會被卸載)和插件代理加載,代理加載其中包含:開機(jī)不加載運(yùn)行時動態(tài)加載卸載、開機(jī)加載之后再動態(tài)卸載加載。
插件選擇被代理時插件可以被動態(tài)卸載和加載,在已經(jīng)被卸載的情況下,當(dāng)外部驅(qū)動事件到來以后被加載。
特定的插件是通過繼承Plugin這個類實(shí)現(xiàn)特定的插件功能,其相對應(yīng)的插件由多態(tài)的形式存在并且流轉(zhuǎn)在插件管理平臺中。
事件處理或者驅(qū)動插件根據(jù)不同需求,需要繼承Plugin并且實(shí)現(xiàn)如下方法或者使用如下的接口:
class Plugin {
public:
/* 插件生命周期中,當(dāng)前插件被平臺加載起來后調(diào)用,可以用于初始化一些數(shù)據(jù) */
virtual void OnLoad();
/* 插件生命周期中,當(dāng)前插件被平臺卸載前調(diào)用,可以用于回收一些數(shù)據(jù) */
virtual void OnUnload();
/* 運(yùn)行期判斷當(dāng)前插件是否被加載,返回true是被需要被平臺加載;false是將永遠(yuǎn)不會被平臺加載。Plugin默認(rèn)返回true */
virtual bool ReadyToLoad();
/* 當(dāng)前插件在流水線上時,接收流水線上外部驅(qū)動事件,或者當(dāng)前插件自己設(shè)置的延時事件,并且處理事件。其處理過程是當(dāng)前插件定義是的線程中,若沒有指定則在當(dāng)前線程進(jìn)行 */
virtual bool OnEvent(std::shared_ptr< Event >& event) override;
/* 當(dāng)前插件在流水線上,且是當(dāng)前流水線上第一個插件時,判斷是否流轉(zhuǎn)整條流水線。返回true是流轉(zhuǎn)整條流水線;false是不流轉(zhuǎn)整條流水線 */
virtual bool CanProcessEvent(std::shared_ptr< Event > event) override;
/* 當(dāng)前插件在流水線上時,判斷當(dāng)前事件源是否進(jìn)入暫停狀態(tài)。返回true是進(jìn)行處理,將會繼續(xù)調(diào)用插件的OnEvent方法;false將進(jìn)入事件源PauseDispatch方法,進(jìn)行處理,然后繼續(xù)執(zhí)行后續(xù)操作 */
virtual bool CanProcessMoreEvents() override;
/* 可以通過hidumper命令行查詢當(dāng)前插件的dump信息,該接口就是實(shí)現(xiàn)能dump出來那些內(nèi)容 */
virtual void Dump(int fd, const std::vector< std::string >& cmds);
/* 當(dāng)前插件作為一個事件動態(tài)訂閱者,接收通過動態(tài)訂閱來的外部驅(qū)動事件,并且處理事件 */
virtual void OnEventListeningCallback(const Event &msg);
// 以下為接口
/* 當(dāng)前插件作為動態(tài)訂閱者時,添加需要訂閱的事件。訂閱的事件分為依靠eventName訂閱和EventIdRange(eventId范圍也可以是全范圍或者單一id)訂閱。type是事件類型 */
void AddEventListenerInfo(uint32_t type, const EventListener::EventIdRange &range = EventListener::EventIdRange(0));
void AddEventListenerInfo(uint32_t type, const std::set< EventListener::EventIdRange > &listenerInfo);
void AddEventListenerInfo(uint32_t type, const std::string& eventName);
void AddEventListenerInfo(uint32_t type, const std::set< std::string > &eventNames);
/* 設(shè)置延時執(zhí)行事件 等時間到了以后在OnEvent中執(zhí)行 */
void DelayProcessEvent(std::shared_ptr< Event > event, uint64_t delay);
/* 得到hiview插件管理平臺接口的上下文 */
HiviewContext* GetHiviewContext();
const std::string& GetName();
const std::string& GetVersion();
void SetVersion(const std::string& version);
}
插件的生命周期
插件目前只有OnLoad和OnUnload兩個生命周期回調(diào)。
這里的dynamic插件僅僅表示單一插件實(shí)體是以so的形式體現(xiàn),名稱為libxxx.z.so。
dynamic插件與上文的動態(tài)加載卸載插件沒有關(guān)系。dynamic插件的so也與[插件包]的so也沒有關(guān)系。
dynamic插件與靜態(tài)插件沒有功能上面的區(qū)別,只區(qū)別插件的實(shí)現(xiàn)形式。
其生命周期調(diào)用邏輯如下
插件的注冊方式:
插件的注冊方式有三種:
名稱 | 說明 | 實(shí)例 |
---|---|---|
靜態(tài)注冊 | 通過使用宏定義REGISTER(xxx);注冊,該種注冊方式的插件不能被卸載 | 實(shí)例詳見test/plugins/examples/event_processor_example1中的插件 |
代理注冊 | 通過使用宏定義REGISTER_PROXY(xxx);注冊,開機(jī)不加載運(yùn)行時動態(tài)加載卸載 | 實(shí)例詳見test/plugins/examples/event_processor_example3中的插件 |
代理注冊且開機(jī)加載 | 通過使用宏定義REGISTER_PROXY_WITH_LOADED(xxx);注冊,開機(jī)加載之后再動態(tài)卸載加載 | 實(shí)例詳見test/plugins/examples/event_processor_example4中的插件 |
事件源的定義
事件源是繼承于EventSource實(shí)現(xiàn)特定的事件源功能。事件源也是一種特殊的插件,EventSource繼承于[Plugin]可以實(shí)現(xiàn)或者使用其中公共的方法。事件源作為事件的驅(qū)動者,不能被代理加載,只能靜態(tài)加載。
事件源需要繼承EventSource這個類實(shí)現(xiàn)如下方法或者使用如下的接口,根據(jù)需求也需實(shí)現(xiàn)[Plugin]類中的虛方法:
class EventSource : public PipelineEventProducer, public Plugin {
public:
/* 開啟事件源,當(dāng)平臺加載完成,加載事件源之后執(zhí)行,可以用于初始化或者開啟一些事件源數(shù)據(jù)或者事件 */
virtual void StartEventSource();
/* 通知事件生產(chǎn)者事件已完成其傳遞 */
virtual void Recycle(PipelineEvent* event);
/* 暫停調(diào)度并安排恢復(fù)調(diào)度 */
virtual void PauseDispatch(std::weak_ptr< Plugin > plugin);
//以下為接口
/* 將事件下發(fā)到各個流水線,開始當(dāng)前事件源下流水線的運(yùn)作 */
bool PublishPipelineEvent(std::shared_ptr< PipelineEvent > event);
/* 動態(tài)添加流水線到當(dāng)前事件源 */
void AddPipeline(std::shared_ptr< Pipeline > pipeline);
};
事件源實(shí)例詳見test/plugins/examples/event_source_example或者test/plugins/examples_bundle/bundle_event_source_example
插件包的概念
根據(jù)業(yè)務(wù)的需求,插件可以不用全部編譯在hiview二進(jìn)制中。支持將若干個插件編譯成為獨(dú)立的插件包,以獨(dú)立so的形式提供,并且一個so對應(yīng)于一個plugin_config文件。 例如libxxx.z.so,對應(yīng)于xxx_plugin_config配置文件。
hiview插件管理平臺插件包做如下說明:
1、插件包作為一個獨(dú)立的個體在插件管理平臺中運(yùn)行,內(nèi)部插件、流水線或者事件源都與在hiview二進(jìn)制中的插件沒有功能上的差異。
2、插件包中的插件可以插入到hiview二進(jìn)制中的流水線中。
3、無論在何處的訂閱者,根據(jù)其訂閱的規(guī)則都可以收到全平臺發(fā)送出來的相應(yīng)事件。
插件跨包注冊流水線實(shí)例詳見test/plugins/examples_bundle/bundle_plugin_example2中的插件
流水線的概念
流水線是用于處理多個插件協(xié)同完成任務(wù)。流水線上面可以對應(yīng)多個插件,流水線上面的執(zhí)行插件的順序是根據(jù)配置文件上寫的先后順序執(zhí)行。
事件源與流水線的概念對應(yīng),關(guān)系為一對多關(guān)系。一個事件源可以有n個流水線,一個流水線可以有n個插件。
事件源在平臺初始化后可以循環(huán)的調(diào)用PublishPipelineEvent方法將事件源新建的事件向綁定的流水線分發(fā),分發(fā)前會向流水線詢問是否處理該事件,這里默認(rèn)使用流水線中的第一個插件的CanProcessEvent接口。如果返回true處理該事件,則會進(jìn)行后續(xù)的處理;如果返回false則當(dāng)前整條流水線不處理該事件。
流水線處理流程詳見整個test/plugins/examples模塊。
hiview配置文件
hiview插件管理平臺通過“[平臺配置文件]”配置相關(guān)“[插件配置文件]”的路徑信息、生成日志路徑等信息。
平臺配置文件
“平臺配置文件”默認(rèn)在設(shè)備/system/etc/hiview/下面的hiview_platform_config,具體內(nèi)容如下:
DEFAULT_PLUGIN_CONFIG_NAME = "plugin_config"
PLUGIN_CONFIG_FILE_DIR = "/system/etc/hiview/"
DYNAMIC_LIB_SEARCH_DIR = "/system/lib/"
DYNAMIC_LIB64_SEARCH_DIR = "/system/lib64/"
WORK_DIR = "/data/log/hiview/"
COMMERCIAL_WORK_DIR = "/log/LogService/"
PERSIST_DIR = "/log/hiview/"
名稱 | 說明 |
---|---|
DEFAULT_PLUGIN_CONFIG_NAME | 表示默認(rèn)的插件配置文件名字,plugin_config表示編譯在hiview二進(jìn)制中插件的配置文件名字,基于此名字 |
DEFAULT_PLUGIN_CONFIG_NAME | xxx_plugin_config表示編譯在libxxx.z.so中插件的配置文件名字 |
PLUGIN_CONFIG_FILE_DIR | 表示插件配置文件所在的位置 |
DYNAMIC_LIB_SEARCH_DIR | 表示插件包在32位系統(tǒng)中的位置 |
DYNAMIC_LIB64_SEARCH_DIR | 表示插件包在64位系統(tǒng)中的位置 |
WORK_DIR | 工作日志路徑 |
PERSIST_DIR | 持久日志路徑 |
插件配置文件
在hiview二進(jìn)制中的插件需要通過編寫build/plugin_build.json文件來配置相關(guān)信息,編譯的時候會自動生成plugin_config文件。
插件配置需要考慮下面幾個部分的內(nèi)容:
名稱 | 說明 |
---|---|
插件信息 | 包含插件類的名稱、插件對應(yīng)的gn的路徑、gn中編譯的名稱 |
平臺配置規(guī)則 | 包含對應(yīng)的rom、ram、加載時間等信息 |
插件加載的順序 | 平臺將安裝配置的先后順序加載。優(yōu)先根據(jù)加載時間排序,若加載時間相同、則按照配置的順序加載。 |
流水線配置 | 如果插件參與流水線,將插件配置到相應(yīng)的流水線下 |
流水線組(事件源)的配置 | 將流水線配置到對應(yīng)的事件源下面 |
線程配置模型 | 配置插件是否需要線程 |
實(shí)例plugin_build.json如下:
{
"plugins": { # 插件列表信息
"SysEventSource": { # 插件名,必須為對應(yīng)的類名(事件源也是插件,也需要在此配置)
"path": "core", # 插件位置,插件gn所在的位置
"name": "sysevent_source" # 插件的編譯名,在gn中插件所對應(yīng)的部分的編譯名
},
"SysEventService": {
"path": "plugins/eventservice",
"name": "sys_event_service"
},
"Faultlogger": {
"path": "plugins/faultlogger",
"name": "faultlogger"
},
"EventLogger": {
"path": "plugins/eventlogger",
"name": "eventlogger"
},
},
"rules": [ # 平臺平臺配置規(guī)則列表
{
"rule": [
{
"rom": "1G", # rom規(guī)則
"ram": "1G" # ram規(guī)則
}
],
"info": {
"loadorder": { # 插件的載入時間配置列表
"SysEventSource": { # 插件名稱,與上面"plugins"列表中的插件名稱對應(yīng)
"loadtime": 0 # 當(dāng)前插件延時多少時間之后加載,為0就是立即加載
}, # 插件加載順序,先按加載時間先后加載,然后按照當(dāng)前列表順序加載。
"Faultlogger": {
"loadtime": 0
},
"EventLogger": {
"loadtime": 0
},
"SysEventService": {
"loadtime": 0
}
},
"pipelines": { # 流水線配置列表
"SysEventPipeline": [ # 流水線名稱
"SysEventService", # 流水線中的插件,與上面"plugins"列表中的插件名稱對應(yīng)
"Faultlogger", # 流水線中插件的執(zhí)行順序按照列表中先后順序執(zhí)行
"EventLogger"
]
},
"pipelinegroups": { # 流水線組(事件源)配置列表
"SysEventSource": [ # 事件源名稱,與上面"plugins"列表中的插件名稱對應(yīng)
"SysEventPipeline"
]
},
"threads": { # 流線程配置模型列表
"singledthread": { # 獨(dú)享線程模式
"SysEventSource": "sysevent_source", # 將對應(yīng)插件配置,名為"sysevent_source"的線程
"SysEventService": "sysevent_service", # 獨(dú)享線程模式
},
"sharedthread": {
"sharedthread1": ["Faultlogger", "EventLogger"] #當(dāng)前兩個插件共享一個名為"sharedthread1"的線程
}
}
}
}
]
}
在獨(dú)立插件包的插件配置文件,由于插件實(shí)體可以不參與整體hiview的編譯,所以直接編寫xxx_plugin_config配置文件。不編寫plugin_build.json
plugin_config實(shí)例完整文件可以參考test/resource/base/plugin_config,文件講解如下:
plugins:7 // 插件總數(shù)是七個,與下面插件個數(shù)必須對應(yīng)
EventProcessorExample4[thread:t-example-2]:0 static // 插件名稱[thread:線程名稱]:加載時間 插件模式
EventProcessorExample1[thread:t-example-4]:0 static // 插件名稱必須是類名
EventProcessorExample2[thread:t-example-1]:0 static // 線程名稱一樣表示這兩個插件共享線程,中括號內(nèi)為空表示不配置線程
EventProcessorExample3[thread:t-example-4]:0 static // 加載時間,延時多長時間以后加載,0為立馬加載
EventProcessorExample5[]:0 static // 插件加載的順序先按照加載時間順序,然后在按照當(dāng)前配置順序加載
EventSourceExample[thread:t-example-3]:0 static // 插件模式,static表示當(dāng)前插件實(shí)體靜態(tài)的編譯在hiview二進(jìn)制中
DynamicLoadPluginExample[thread:t-example-5]:0 dynamic // 插件模式,dynamic表示當(dāng)前插件實(shí)體是以so的形式體現(xiàn),名稱為libDynamicLoadPluginExample.z.so
pipelines:3 // 表示當(dāng)前流水線有三個,與下面流水線個數(shù)必須對應(yīng)
NormalPipeline:EventProcessorExample1 EventProcessorExample2 EventProcessorExample3 EventProcessorExample4 // 流水線名稱:插件1 插件2 插件3 插件4(插件名需要與上面定義的插件對應(yīng))
ReversePipeline:EventProcessorExample4 EventProcessorExample3 EventProcessorExample2 EventProcessorExample1 // 流水線上插件的執(zhí)行順序是按照這個的先后順序
SimplePipeline:EventProcessorExample3 EventProcessorExample2 // 流水線名稱注意不要跟其他包的流水線重復(fù)
pipelinegroups:1 // 流水線組(事件源)列表,該表示,事件源有1個
EventSourceExample:NormalPipeline ReversePipeline SimplePipeline // 事件源:流水線1 流水線2 流水線3 (流水線名需要與上面定義的流水線對應(yīng))
// 事件源執(zhí)行流程,按照先后順序執(zhí)行流水線,每個流水線按其在的插件的順序執(zhí)行
// 前一個插件流水線中所以的插件執(zhí)行完畢,才能只能下一個流水線
插件平臺提供的能力
事件
插件與插件之間,插件與平臺之間的交互均采用事件驅(qū)動的模式,事件定義請參考文件base/include/event.h,這里不詳細(xì)講解。
事件預(yù)定義的若干類型,在Event::MessageType中,業(yè)務(wù)也可以在includepublic_defines.h中定義自己的事件類型。
事件訂閱
插件與插件之間,或者插件與單獨(dú)的訂閱者模塊之間,可以通過事件訂閱的方式進(jìn)行交互。
事件訂閱分為兩種:
一種是單獨(dú)的訂閱者模式EventListener,該模式可以單獨(dú)形成一個EventListener模塊專門訂閱事件、處理事件的小模塊;也可以被插件繼承用于插件監(jiān)聽事件,當(dāng)插件選擇這種監(jiān)聽模式將不能被動態(tài)加載和卸載
需要繼承EventListener并且實(shí)現(xiàn)如下方法或者使用如下的接口:
class EventListener {
public:
/* 該方法已經(jīng)棄用,為了兼容以前的代碼沒有刪除,新代碼中不需要實(shí)現(xiàn) */
virtual bool OnOrderedEvent(const Event &msg);
/* 該方法為接收訂閱來的外部驅(qū)動事件,并且處理事件 */
virtual void OnUnorderedEvent(const Event &msg) = 0;
/* 該方法為定義當(dāng)前訂閱者的名字 */
virtual std::string GetListenerName() = 0;
// 以下為接口
/* 添加需要訂閱的事件。訂閱的事件分為依靠eventName訂閱和EventIdRange(eventId范圍也可以是全范圍或者單一id)訂閱。type是事件類型 */
void AddListenerInfo(uint32_t type, const EventListener::EventIdRange &range = EventListener::EventIdRange(0));
void AddListenerInfo(uint32_t type, const std::set< EventListener::EventIdRange > &listenerInfo);
void AddListenerInfo(uint32_t type, const std::string& eventName);
void AddListenerInfo(uint32_t type, const std::set< std::string > &eventNames);
/* 可以獲取當(dāng)前訂閱者訂閱的事件信息 */
bool GetListenerInfo(uint32_t type, std::set< std::string > &eventNames);
bool GetListenerInfo(uint32_t type, std::set< EventListener::EventIdRange > &listenerInfo);
};
EventListener訂閱者實(shí)例詳見test/plugins/examples/event_processor_example2中的插件
另一種是在插件中動態(tài)訂閱者,可以被動態(tài)卸載和加載。當(dāng)插件是在代理模式的時候,訂閱的事件到來可以動態(tài)加載起來插件進(jìn)行執(zhí)行,過一點(diǎn)時間以后動態(tài)插件再被卸載。
插件中動態(tài)訂閱者實(shí)例詳見test/plugins/examples/event_processor_example4中的插件
注意:區(qū)分兩種訂閱模式的接口,不要混合調(diào)用了,混合調(diào)用添加的訂閱事件將不會被另一種方式監(jiān)聽到。
平臺提供的功能接口
平臺提供的功能在插件中可以通過調(diào)用GetHiviewContext()獲得平臺接口上下文。平臺提供的主要的功能接口如下:
class HiviewContext {
public:
/* 將繼承了EventListener類的訂閱者,注冊到平臺中,開啟訂閱 */
void RegisterUnorderedEventListener(std::weak_ptr< EventListener > listener);
/* 將作為插件中的動態(tài)訂閱者,注冊到平臺中,開啟訂閱。動態(tài)插件和靜態(tài)插件只要是想通過插件內(nèi)部接口和方法定義的訂閱者都可以使用這個注冊 */
void RegisterDynamicListenerInfo(std::weak_ptr< Plugin > listener);
/* 發(fā)送事件到訂閱者,該接口發(fā)送的事件,無論那種訂閱方式都可以按規(guī)則被訂閱到 */
void PostUnorderedEvent(std::shared_ptr< Plugin > plugin, std::shared_ptr< Event > event);
/* 卸載并從平臺移除插件,之后再也不能被加載起來,慎用 */
void RequestUnloadPlugin(std::shared_ptr< Plugin > caller);
/* 獲得平臺中共享的EventLoop線程 */
std::shared_ptr< EventLoop > GetSharedWorkLoop();
/* 按照流水線名稱獲得流水線當(dāng)中的插件 */
std::list< std::weak_ptr< Plugin >> GetPipelineSequenceByName(const std::string& name);
/* 檢測平臺是否已經(jīng)全部加載完畢,返回true是全部初始化和加載完畢,返回false就是平臺沒有初始化和加載完成 */
bool IsReady();
/* 按照key值獲得平臺參數(shù) */
std::string GetHiviewProperty(const std::string& key, const std::string& defaultValue);
/* 可以向平臺中設(shè)置平臺參數(shù) key-value形式 */
bool SetHiviewProperty(const std::string& key, const std::string& value, bool forceUpdate);
/* 向平臺的特定流水線中添加特定插件 */
void AppendPluginToPipeline(const std::string& pluginName, const std::string& pipelineName);
/* 通過插件名稱獲取插件或者插件的代理 */
std::shared_ptr< Plugin > GetPluginByName(const std::string& name);
};
實(shí)例解析
這里以test/plugins/examples/event_source_example事件源為例講解其注冊、流轉(zhuǎn)、監(jiān)控等機(jī)制。
//event_source_example.h
// 該類繼承FileDescriptorEventCallback,可以監(jiān)聽文件夾文件的變化,使用該類方法必須配置線程
class EventSourceExample : public FileDescriptorEventCallback, public EventSource {
public:
EventSourceExample();
~EventSourceExample();
/* 為了記錄當(dāng)前全局的插件構(gòu)造和析構(gòu)的數(shù)據(jù)結(jié)構(gòu) */
static std::set< std::string > count;
/* 重載事件源相關(guān)的虛方法,詳情參考[事件源的定義]一節(jié) */
void OnLoad() override;
void OnUnload() override;
void Recycle(PipelineEvent* event) override;
void PauseDispatch(std::weak_ptr< Plugin > plugin) override;
void StartEventSource() override
{
/* 添加文件監(jiān)聽到eventLoop線程 */
GetWorkLoop()- >AddFileDescriptorEventCallback("Example",
std::static_pointer_cast< EventSourceExample >(shared_from_this()));
}
/* 監(jiān)聽的文件夾文件發(fā)生變化的回調(diào) */
bool OnFileDescriptorEvent(int fd, int type) override;
/* 初始化需要監(jiān)聽的文件夾文件 */
int32_t GetPollFd() override;
/* 返回Poll類型 */
int32_t GetPollType() override;
const static inline int PIPELINE_EVENT_ID_AAA = 901000000;
const static inline int PIPELINE_EVENT_ID_BBB = 901000001;
const static inline int PIPELINE_EVENT_ID_CCC = 901000002;
const static inline int PIPELINE_EVENT_ID_TAA = 901000010;
private:
/* 文件創(chuàng)建方法 */
void CreateWatchFile(const std::string& path);
/* 創(chuàng)建事件方法 */
void CreateAndPublishEvent(const std::string& file);
const static inline std::string SYSTEM_FAULT_LOG_PATH = "/data/test/faultlog";
int inotifyFd_;
std::map< std::string, int > fileMap_;
};
鴻蒙OpenHarmony知識已更新 ←前往
-
鴻蒙系統(tǒng)
+關(guān)注
關(guān)注
183文章
2638瀏覽量
66707 -
鴻蒙
+關(guān)注
關(guān)注
57文章
2392瀏覽量
43050 -
HarmonyOS
+關(guān)注
關(guān)注
79文章
1982瀏覽量
30575 -
OpenHarmony
+關(guān)注
關(guān)注
25文章
3744瀏覽量
16578 -
鴻蒙星河
+關(guān)注
關(guān)注
0文章
8瀏覽量
73
發(fā)布評論請先 登錄
相關(guān)推薦
評論