基于SMAGR_BootStrap函数谈整个SA服务框架的启动


该篇文章从SMAGR_BootStrap函数出发去探究整个SA服务框架的启动过程
首先我们看到一个存储信息的结构体SAStore

1. SA_Store——服务信息存储

saStore主要存储了service和feature相关的信息数据(包括service的handle和feature的token等),数据结构为循环链表,通过pid可以唯一定位一个service和maps中的一项数据体,在功能函数中使用了插入排序根据pid的大小对maps的数据项进行正向排序,使用二分查找对maps中数据项进行查找

其结构体嵌套较为复杂,所以将其展开平铺,便于便于读者更好的理解结构体之间的嵌套关系:
在这里插入图片描述


之后我们正式进入SAMGR_BootStrap的解读

2. SAMGR_BootStrap——服务的初始化

在注册完系统服务和APP服务后,我们通过SAMGR_Bootstrap函数进行smagr的启动,将所有未初始化的服务进行初始化,使得整个系统进入DYNAMIC平稳运行的状态。我们将其拆分为几个函数模块来看


2.1 void SAMGR_Bootstrap(void)——主函数

首先我们宏观把握这个函数做了哪些事,后面再深入具体函数来看它是如何实现这个功能的
在这里插入图片描述
函数流程:
1.得到前面创建好的g_samgrImpl并对其进行mutex的检查
2. 创建一个临时的Vector用于保存后面检查出的未初始化的服务
3. 系统对应的BootStatus进入下一个状态
4. 获取互斥锁,开始对共享数据的修改和访问
5. 遍历samgr->services中的每个服务
6. 找到其中所有没有完成初始化的服务将其serviceImpl指针添加到上面的临时Vector中
7. 互斥锁的释放
8. 调用InitializeAllServices将Vector传入,初始化所有服务
9. 调用VECTOR_Clear将临时Vector释放
10. 调用InitCompleted检查是否全部初始化完成
11. 调用HILOG_INFO返回系统状态可以往下一步

注意:函数InitializeAllServices和InitCompleted所做的工作还有很多,包括taskPool的创建,消息处理相关,后面会深入函数进行分析


2.2 总结——服务初始化的三步走

同样通过图示的方式将流程更加清晰地展现
在这里插入图片描述
一个完整的服务的创建和运行需要进行三步走:

  1. 通过SYS_SERVICE_INIT进行服务的注册
  2. 通过SYS_FEATURE_INIT进行feature和featureApi的注册(如果存在feature)
  3. 通过SAMGR_BootStrap函数将服务初始化,分配taskPool并初始化messageHandle

经过以上三步一个服务就真正创建完毕了

而一个完整的系统的创建和运行也是三步走:

  1. 完成所有系统服务和相关feature的注册(包括三个系统服务:BootStrap、Broadcast和hiview)
  2. 完成所有用户自定义的APP服务和相关feature的注册
  3. 通过sendBootRequest启动SMAGR_BootStrap将所有注册但是未初始化启动的服务进行初始化启动

下面从两个重要的函数InitializeAllServices和InitCompleted入手具体看服务的初始化是如何实现

3. InitializeAllServices——实现所有服务的初始化

3.1 整体解读

3.1.1 InitializeAllServices

首先我们从整体出发来看看InitializeAllServices都干了哪些工作
在这里插入图片描述
函数流程:主要是两个for循环
1.在第一个for循环中,利用VECTOR_At得到Vector中管理的服务serviceImpl
2.然后通过service实现的两个函数GetTaskConfig和GetName获得taskConfig和字符串服务名字
3.根据serviceImpl、taskConfig、serviceName调用AddTaskPool进行三类taskPool的创建
4.再调用InitializeSingleService对每个serviceImpl进行服务初始化
5.第二个for循环中,同样拿到service对应的serviceImpl和name
6.对于每一个serviceImpl调用SAMGR_StartTaskPool开启线程池和队列任务

这里需要注意一点:为什么要分为两个for循环来做呢?——从代码中我们可以看到第二次for循环在获取了互斥锁之后进行操作的——说明涉及到共享数据或者结构体的修改和读写,而上面的for循环并不需要,所以分成了两个for循环

给出InitializeAllServices的流程图供读者参考
在这里插入图片描述


3.1.2 重要结构体TaskConfig和TaskPool

这里我们看到一个新的结构体类型TaskConfig

struct TaskConfig {
    int16 level;//多服务共享任务的分类level 在enum SpecifyTag中有详细取值
    int16 priority;//任务优先级 在enum TaskPrioriy中有详细取值
    uint16 stackSize;//任务堆栈的大小
    uint16 queueSize;//任务队列的大小
    uint8 taskFlags;//任务标志 在enum taskType中有详细取值
};

typedef enum SpecifyTag {
    LEVEL_HIGH = 0,
    LEVEL_MIDDLE = 1,
    LEVEL_LOW = 2,
    LEVEL_CUSTOM_BEGIN,
} SpecifyTag;

//鸿蒙原本的注释就比较详细
typedef enum TaskPriority {
    /** Low-priority: (9, 15) */
    PRI_LOW = 9,
    /** Lower than the normal priority: [16, 23) */
    PRI_BELOW_NORMAL = 16,
    /** Normal priority: [24, 31). The log service is available. */
    PRI_NORMAL = 24,
    /** Higher than the normal priority: [32, 39). The communication service is available. */
    PRI_ABOVE_NORMAL = 32,
    /** Upper limit of the priority */
    PRI_BUTT = 39,
} TaskPriority;

//任务类型——也就是在AddTaskPool中出现的三类taskPool
typedef enum TaskType {
    /** Tasks shared based on their priority by services */
    SHARED_TASK = 0,
    /** Task exclusively occupied by a service */
    SINGLE_TASK = 1,
    /** A specified task shared by multiple services */
    SPECIFIED_TASK = 2,
    /** No task for the service. Generally, this situation does not occur. */
    NO_TASK = 0xFF,
} TaskType;

重要结构体TaskPool
在这里插入图片描述

在后面讲解AddTaskPool中会对两个结构体进行解读

对于初始化的解读就自然落在了下面三个重要函数身上

3.2 三个重要函数

3.2.1 AddTaskPool

/*
函数功能:三类TaskPool的创建
函数参数:service:taskPool对应的服务 cfg:taskPool的参数设置 name:服务的字符串名称
函数返回:void
*/
static void AddTaskPool(ServiceImpl *service, TaskConfig *cfg, const char *name)
{
    if (service->taskPool != NULL) {
        return;
    }

    if (cfg->priority < PRI_LOW || cfg->priority >= PRI_BUTT) {
        HILOG_ERROR(HILOG_MODULE_SAMGR, "The %s service pri(%d) is out of range!", name, cfg->priority);
        cfg->priority = PRI_LOW;
    }

    //三类taskPool的处理
    switch (cfg->taskFlags) {
        case SHARED_TASK: {
            int pos = (int)cfg->priority / PROPERTY_STEP;//PROPERTY_STEP:8
            SamgrLiteImpl *samgr = GetImplement();
            if (samgr->sharedPool[pos] == NULL) {
            	//{LEVEL_HIGH, (int16) ((pos) * 8 + 1), 0x800, 25, SHARED_TASK}
                TaskConfig shareCfg = DEFAULT_TASK_CFG(pos);
                //为服务创建相应的队列和TaskPool
                samgr->sharedPool[pos] = SAMGR_CreateFixedTaskPool(&shareCfg, name, DEFAULT_SIZE);
            }
            service->taskPool = samgr->sharedPool[pos];
            //用于检测taskPool中的ref是否为空或者超过最大值
            if (SAMGR_ReferenceTaskPool(service->taskPool) == NULL) {
                HILOG_ERROR(HILOG_MODULE_SAMGR, "shared task:%p pri:%d ref is full", service->taskPool, cfg->priority);
                samgr->sharedPool[pos] = NULL;
            }
        }
            break;

        case SPECIFIED_TASK:
            //根据传入的config寻找config一致的referenceTaskPool
            service->taskPool = GetSpecifiedTaskPool(cfg);
            if (service->taskPool != NULL) {
                break;
            }

        //每个service单独使用的taskpool
        case SINGLE_TASK:
            service->taskPool = SAMGR_CreateFixedTaskPool(cfg, name, SINGLE_SIZE);
            break;
        default:
            break;
    }

    if (service->taskPool == NULL) {
        HILOG_ERROR(HILOG_MODULE_SAMGR, "Service<name:%s, flag:%d> create taskPool failed!", name, cfg->taskFlags);
    }
}

函数说明:
1.首先对于传入的config中的priority进行规范检测,不规范的进行日志输出并将其改为PRI_LOW(最低级的优先级)

2.switch-case语句,根据config中的taskFlags判断其类型并调用不同函数进行taskPool的创建

3.对于SHARED_TASK来说,它是由g_samgrImpl管理的shareTask,也就是说——对于用于该类型taskPool的服务来说,该服务的taskPool是在g_samgrImpl共享的。
在代码中我们也可以看到:当g_samgrImpl.sharedPool[pos]对应为NULL则调用DEFAULT_TASK_CFG创建一个高优先级的sharedTask类型的config,并调用SAMGR_CreateFixedTaskPool为服务创建相应的队列和taskPool。如果原本g_samgrImpl.sharedPool[pos]就存在了taskPool则直接service->taskPool = samgr->sharedPool[pos]建立共享池的关系。(另外这里的pos是通过cfg->priority / PROPERTY_STEP计算出来的,可取的值为1234,对应四种优先级的taskPool)

所以共享池的共享仍然是根据taskConfig中的不同优先级priority进行分配的,使用服务的共享池有利于提高OS的处理效率(函数SAMGR_CreateFixedTaskPool在下面会提到)

4.对于SPECIFIED_TASK来说,它是多个服务共享的一个任务池的类型。在代码中调用GetSpecifiedTaskPool,根据传入的config寻找referenceTaskPool,并将其赋给service->taskPool。对于如何找到相应的TaskPool则是调用函数GetSpecifiedTaskPool对所有的serviceImpl的taksPool的config和所给定的config进行memcmp和SAMGR_ReferenceTaskPool,最后让service.taskpool指向该config的taskPool

5.对于SINGLE_TASK,它是一个服务单独占用一个任务池。所以直接简单的调用SAMGR_CreateFixedTaskPool将新建的taskPool赋给service->taskPool即可

三类任务池的设置是为了对不同的服务进行更好的资源分配,设置优先级和level的目的也是如此

综上所述:AddTaskPool的实质是对于不同类型taskPool创建的不同处理

这里同样给出流程图供读者参考
在这里插入图片描述


3.2.2 InitializeSingleService

在这里插入图片描述
分两步走:
1. 如果传入的service没有对应的taskPool则调用DEFAULT_Initialize进行默认初始化处理
2. 否则调用SAMGR_SendSharedDirectRequest传入HandleInitRequest交给对应的线程进行初始化处理 (处理的时候同样也调用了DEFAULT_Initialize)

而DEFAULT_Initialize实质上是进行serviceApi的注册

总的来说serviceApi的注册完成了两件事:第一如果g_remoteRegister不存在,则初始化g_remoteRegister;第二在g_remoteRegister中创建一个服务endpoint并初始化相应的router和policy供终端间的通信使用
具体的终端endpoint代理proxy机制这里不再展开


3.2.3 SAMGR_StartTaskPool

在完成了taskPool和serviceApi的注册后,来到最后一步——taskPool的启动
贴上代码注释:
在这里插入图片描述
函数的逻辑也比较清晰
1.首先是attr的创建
2.然后是调用THREAD_Create进行线程的创建(重要的任务入口函数TaskEntry)
3.再是在tasks中记录线程Id——便于以后的调用
4.最后就是根据size的大小,为一个taskPool创建多个thread,直到top>=size为止

具体task的运行机制大家可以自行阅读

函数流程图:
在这里插入图片描述
最后总结一下InitializeAllServices实现的具体内容:
它主要实现三件事来初始化所有的服务:
第一,三类taskPool的添加和创建,包括队列的创建;
第二,针对service和对应的feature进行serviceApi的注册;
第三,为创建的taskPool创建多个线程启动任务池


4. InitCompleted——检测服务是否全部初始化完毕并推进系统状态

首先还是从这个函数的源代码讲起,贴上代码注释:
在这里插入图片描述
基本的函数流程:
1. 调用GetImplement得到g_samgrImpl
2. 调用GetUninitializedPos得到g_samgrImpl中没有初始化的服务在vector的位置pos
3. pos<size时说明存在未初始化的service,返回EC_SUCCESS
4. 若所有的服务都初始化完成,则查看g_samgrImpl的状态:如果是系统服务初始化完成则进入用户APP服务注册和初始化的状态,调用SendBootRequest进行boot请求
5. 如果是用户APP初始化完成,则进入系统DYNAMIC的状态,同样调用SendBootRequest进行boot请求

在函数SAMGR_Bootstrap中注意到最后的判断是:

int32 err = InitCompleted();
    if (err != EC_SUCCESS) {
        HILOG_INFO(HILOG_MODULE_SAMGR, "Goto next boot step return code:%d", err);
    }

也就是说当其返回值等于EC_SUCCESS时,说明本阶段的初始化工作没有完成;不等于EC_SUCCESS,则进行HILOG_INFO告知系统进入下一个boot阶段——在InitCompleted中初始化完成后就可以调用后面的SendBootRequest来进行下一个阶段的boot工作这是相呼应的

而在源代码的解析中我们可以发现SendBootRequest中使用的handler函数竟然也是SAMGR_Bootstrap,构成了一个类似循环但不是循环的结构:
首先外部调用SYS_INIT进行系统服务的注册,然后利用SAMGR_Bootstrap进行服务的初始化,然后在InitCompleted中检测是否全部初始化完毕,完毕则调用SendBootRequest等待用户APP注册完毕后,再次使用(handler)SAMGR_Bootstrap进行用户APP的初始化,再次调用InitCompleted检查是否全部初始化,完毕再次调用SAMGR_Bootstrap直到系统状态变为DYNAMIC平稳运行态后停止

最后同样给出函数的流程框图,帮助读者更好的理解其中的逻辑:
在这里插入图片描述


5. 基于SA框架完整的进程通信机制

经过对全部代码的解读,结合五大模块的功能和原理,最后以图示和文字说明的方式再一次完整的阐明整个SA框架下的进程通信机制

在这里插入图片描述
整个流程大致如下:
1. 首先通过整个服务启动的三步走完成系统服务启动
2. 每个服务对应有默认的serviceApi或者有0个、1个或多个featureImpl对应的featureApi其内部机制就是对应的endpoint管理着proxy、router、policy
3. 通过proxy代理开启listen,然后创建一个boss线程进行receive并startLoop
4. 再调用Dispatch根据policy进行信息的规范然后对应router信息发送到对应进程的endpoint上
5. 接收endpoint通过receive接收后调用HandleIPC通过proxy,根据发送message中的operation调用不同的handle进行处理并将reply写入缓冲区发送回endpoint完成一次IPC

至此Samgr_Lite的全部代码解读完毕,当然几篇文章并不足以详尽的写完整个SA服务框架,更多的细节和功能待读者发掘!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

国家一级假勤奋研究牲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值