Hi3516的SAMGR--系统服务框架子系统-6-系统服务的启动 原创 精华

liangkz_梁开祝
发布于 2021-6-26 12:22
浏览
3收藏

Hi3516的SAMGR--系统服务框架子系统-6

系统服务的启动

liangkz 2021.06.26

 

在进入正题之前,我们先来简单了解一下在系统服务的启动方式上,轻量系统和小型系统(包括了标准系统,后面一样)的差别。

 

不管是轻量系统还是小型系统,系统服务和特性(service/feature)的Init函数,都会用SYS_SERVICE_INIT /SYS_FEATURE_INIT或者SYSEX_SERVICE_INIT/APP_SERVICE_INIT/SYSEX_FEATURE_INIT/APP_FEATURE_INIT等一组宏来做标记,这组宏定义在 //utils/native/lite/include/ohos_init.h文件内。

打开这个文件,可以看到

Hi3516的SAMGR--系统服务框架子系统-6-系统服务的启动-鸿蒙开发者社区

在对“LAYER_INITCALL”这个宏的展开上,轻量系统和小型系统的差别就开始了。

命令行进入B_LTS项目代码根目录,执行$find ./ -name *.gn | xargs grep -in "LAYER_INIT_SHARED_LIB" 指令,可以查到"LAYER_INIT_SHARED_LIB"定义的地方,在 //foundation/distributedschedule/samgr_lite/samgr/BUILD.gn:14 

打开看一下:

config("external_settings_shared") {

  defines = [ "LAYER_INIT_SHARED_LIB" ]

}

if (ohos_kernel_type == "liteos_m") {

  static_library("samgr") {

......

}

}

if (ohos_kernel_type == "liteos_a" || ohos_kernel_type == "linux") {

  shared_library("samgr") {

......

    public_configs += [ ":external_settings_shared" ]

  }

}

也就是说轻量系统没有定义"LAYER_INIT_SHARED_LIB",而小型系统则是包含这个宏的定义的,为什么要将"LAYER_INIT_SHARED_LIB"的定义要放在samgr_lite/samgr组件上,这个稍后会讲到。

 

  • 轻量系统:

轻量系统上,LAYER_INITCALL使用__attribute __((section(“section_name”)))来展开, 其作用是在编译阶段,将函数或数据链接到指定的名为"section_name"对应的段中。在系统启动时,再通过MODULE_INIT/SYS_INIT宏引导去"section_name"对应的段中找到记录的入口函数,并依次执行它们。

注意,这种启动方式是串行的,前一个服务的入口函数没有执行完,并不会执行后一个服务的入口函数,这就是不能在SYS_SERVICE_INIT /SYS_FEATURE_INIT等宏标记的入口函数做太多事情、特别是不能做阻塞式的事情的原因。

更具体的启动细节,请去《Hi3861_WiFiIoT工程的一点理解》进行阅读理解。

 

  • 小型系统:

小型系统上,LAYER_INITCALL使用__attribute __((constructor))来展开,对应还有一个__attribute __((destructor)),它们就类似于C++的类中的构造函数和析构函数的作用。

如果函数被设定为constructor属性,则该函数会在main()函数开始执行之前被自动地先执行;

如果函数被设定为destructor属性,则该函数会在main()函数执行结束之后或者exit()被调用后,被自动地执行。

这在小型系统中非常有用,比如某个进程的运行依赖ServiceA和FeatureAa,ServiceA和FeatureAa的Init函数都用SYS_SERVICE_INIT /SYS_FEATURE_INIT标记了,那在这个进程启动时的main函数执行之前,ServiceA和FeatureAa的Init函数就会先于main被执行。所以,小型系统中,梳理清楚关键进程的服务依赖关系就显得非常重要了。

 

下面我们就进入正题,小型系统的系统服务的整体的启动流程。

我们知道,鸿蒙系统从内核态切换到用户态,跑的第一个进程Init,也叫用户态根进程,它读取并分析/etc/init.cfg配置文件(//vendor/hisilicon/hispark_taurus/init_configs/init_liteos_a_3516dv300.cfg的副本),根据上面的记录,按顺序启动几个关键的系统进程:

{

            "name" : "init",

            "cmds" : [

                "start shell",

                "start apphilogcat",

                "start foundation",

                "start bundle_daemon",

                "start appspawn",

                "start media_server",

                "start wms_server",

                "start hiview",

                "start sensor_service",

                "start ai_server"

            ]

},

系统启动的log如下:

Hi3516的SAMGR--系统服务框架子系统-6-系统服务的启动-鸿蒙开发者社区

这里的“[init_service] ServiceStart:[[xxxx]]:OK, pid[x].”仅仅表明用户态根进程已经为服务进程创建好了运行的基础环境并拿到了服务进程的Pid,并不代表服务进程已经完全可以正常工作了。服务进程需要去执行自己的main()函数跑起来,而在这之前,又需要先去跑完它依赖的所有的“ServiceA和FeatureAa”的__attribute __((constructor))函数,然后main()的最后一步,一般是进入while (1) { pause() }状态,也就是服务进程进入后台,默默守护着本进程以及它的所有子进程/子线程,有点像蜂王/蚁后一样,进程对内/外提供的服务、功能、接口等具体工作,由它的子子孙孙来完成。

上面提到,一个服务进程对别人的依赖关系非常重要,下表是我对上面init OK的9个进程的依赖关系所做的一些梳理,把每一个进程的依赖关系一层层找到列了出来,从上到下,把对三方库的依赖、重复出现的依赖,都灰化掉了,这样看起来就清爽多了。【每次重洗整理都发现会有一些小小的疏漏,所以这个表格还不算完美】

Hi3516的SAMGR--系统服务框架子系统-6-系统服务的启动-鸿蒙开发者社区

从系统启动完整过程的log看这9个进程的实际启动顺序与上面/etc/init.cfg给出的顺序并不一一对应:

 Line 167: {Process:shell}

 Line 170: {Process:apphilogcat}

 Line 182: {Process:sensor_service}

 Line 195: {Process:bundle_daemon}

 Line 284: {Process:ai_server}

 Line 345: {Process:wms_server}

 Line 347: {Process:media_server}

 Line 573: {Process:foundation}

 Line 1678: {Process:appspawn}

这是因为有些进程依赖关系很少,就启动得快一些,而foundation进程,依赖非常多的东西,所以启动得非常慢。

前面说到“为什么要将 LAYER_INIT_SHARED_LIB 的定义要放在samgr_lite/samgr组件上”,因为轻量系统不用__attribute __((constructor))这个机制,LAYER_INIT_SHARED_LIB的定义放在哪里,对轻量系统来说无所谓;而小型系统,看看上面的依赖关系表,shell/apphilogcat/media_server三个进程不依赖其他的ServiceA和FeatureAa,也不依赖samgr组件,所以对它们来说,LAYER_INIT_SHARED_LIB的定义放在哪里,也无所谓;而其他进程都需要依赖samgr,并且是由samgr来管理和启动该进程所依赖的所有ServiceA和FeatureAa,所以LAYER_INIT_SHARED_LIB的定义,最低层次放在samgr是非常合适的,可以把系统组件的耦合程度降到最低。

 

3号进程shell由内核提供,直接启动,这是full shell,代码在//kernel/liteos_a/shell/full/目录下。

4号进程apphilogcat,依赖关系非常简单,仅仅依赖一个三方库和由 //base/hiviewdfx/hilog_lite/frameworks/featured/ 目录下的代码编译生成的动态链接库hilog_shared,所以也是直接启动了。

5号进程foundation,依赖的东西非常多,需要到很后面才会跑到main函数,这个后面详细讲。

反倒是依赖关系相对简单的10号进程sensor_service、6号进程bundle_daemon、11号进程ai_server首先跑完依赖关系,进入main的while (1) { pause() } 状态。

9号进程wms_server,启动起来之后,它的子线程就无休止地监测按键/屏幕触控等输入性质或者窗口显示性质的事件,并做出响应。

8号进程media_server,媒体服务进程看上去是最干净的,只由//device/hisilicon/hardware/multimedia:libhdi_media提供支持,最终由liteos_a:kernel提供支持。

7号进程appspawn,是最后启动的系统进程,它起来之后,就可以通过它孵化出第一个应用进程launcher,以及后面其他的应用进程。

 

上面shell/apphilogcat/media_server三个进程不依赖于samgr,也不依赖于其他的Service/Feature,我们这里就先不说了。

其他6个系统进程,每个进程都有自己独立的地址空间,互相之间不能直接访问,需要通过进程间通信来进行交互。

独立的系统进程,在自己独立的地址空间内,启动自己所依赖的Service/Feature,由自己空间内的SamgrLiteImpl g_samgrImpl来管理,完全就是一个独立的轻量系统,这里可以去看看我前面的文章《Hi3861的SAMGR--系统服务框架子系统-3》,但注意还是会有一些较大的差异。

 

下面以不算简单,但又不复杂的wms_server进程为例,进行分析。

打开 //foundation/graphic/wms/BUILD.gn 文件,查看它的依赖关系,简化整理如下:

Hi3516的SAMGR--系统服务框架子系统-6-系统服务的启动-鸿蒙开发者社区

hdi_input/lite_graphic_hals,需要外围硬件设备的支持,硬件驱动启动的时候就会加载运行了。

samgr和lite_ipc_adapter,都没有__attribute __((constructor))修饰,这是两个独立的动态链接库,装载进来就直接用,注意,是samgr自己而已,并不包含"samgr_server:server"和"samgr_client:client"。

Broadcast服务,包含了“Provider and subscriber”feature,不过好像实际上并没有使用到。

wms_server进程还依赖本进程代码中提供的WMS/IMS服务,都没有feature。

 

整理的表格如下:

Hi3516的SAMGR--系统服务框架子系统-6-系统服务的启动-鸿蒙开发者社区

从上表的依赖关系来看,它就依赖一个broadcast 组件提供的Service/Feature,以及它自己wms_server组件所提供的Service:WMS/IMS,所以从实际跑起来的log可以验证,在main()跑起来之前,__attribute __((constructor))修饰的Servic/Feature的Init函数先跑起来:

Hi3516的SAMGR--系统服务框架子系统-6-系统服务的启动-鸿蒙开发者社区

在main函数跑起来之前,wms_server进程地址空间内,已经有一张由本进程的SamgrLiteImpl g_samgrImpl控制的静态展开图了(这里就不放展开图了,可以参考《Hi3861的SAMGR--系统服务框架子系统-2》的展开图进行理解)。

main( )中,跑完HiFbdevInit()/InitDriver()后,会通过HOS_SystemInit()来调用SAMGR_Bootstrap()函数,为上面三个service:Broadcast/WMS/IMS 创建各自的 TaskPool、Task、Queue,其中WMS/IMS是同级别的SHARED_TASK,所以它们共用一个Task/Queue,在收发消息时将会通过ServiceID来做区分。Task运行起来之后,立即监听自己的消息队列,处理消息。

Hi3516的SAMGR--系统服务框架子系统-6-系统服务的启动-鸿蒙开发者社区

三个service:Broadcast/WMS/IMS的消息队列,各自会首先收到一个MSG_DIRECT消息,由HandleInitRequest()来处理,这个HandleInitRequest() 内,会调用DEFAULT_Initialize(serviceImpl)来初始化Service/Feature。

        DEFAULT_Initialize(serviceImpl) 函数内的行为,对于轻量系统来说,就是调用Service/Feature的生命周期函数Initialize()/OnInitialize()来完成初始化工作,就OK了,SAMGR_RegisterServiceApi() 函数是空的桩函数。但是对于小型系统来说,SAMGR_RegisterServiceApi()有另外的实现,它是进入一个新世界的入口,这里先点到为止,后面我们会详细讲。

 

至此,系统服务进程wms_server就可以开始工作了。

“[wms.cpp] main[5-4]: GetInstance()->Run()”这一步,InputManagerService instance对象就开始无休止地监测按键/屏幕触控等输入性质的事件,并做出响应。

“[wms.cpp] main[5-5]: while(1)”这一步,也通过“OHOS::LiteWM::GetInstance()->MainTaskHandler();”开始无休止地根据需要重绘窗口显示内容。

 

其他五个进程,启动情况类似,或更简单一些,或更复杂一些,但都会跑类似的流程,各个进程内部,都由自己的SamgrLiteImpl g_samgrImpl来管理本进程内的Service/Feature,进程内部的Service线程,直接通过消息队列机制来进行通信即可,要想跨进程进行交互,那就需要更复杂的机制了。

 

 

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
已于2021-6-26 13:07:42修改
2
收藏 3
回复
举报
4条回复
按时间正序
/
按时间倒序
Anzia
Anzia

用表格做笔记,学到了666

回复
2021-6-26 14:09:03
liangkz_梁开祝
liangkz_梁开祝 回复了 Anzia
用表格做笔记,学到了666

字不如表,表不如图。

回复
2021-6-26 15:24:22
Anzia
Anzia 回复了 liangkz_梁开祝
字不如表,表不如图。

是啊,思维导图适合处理文字。但是程序逻辑的话,还是用UML图比较好

3
回复
2021-6-26 15:44:53
丨张明亮丨
丨张明亮丨

细致啊,好文

回复
2021-6-26 21:13:02
回复
    相关推荐