如何使用InitializeAllServices函数初始化所有服务?
在完结篇中我们讲述了函数SAMGR_Bootstrap在系统完成系统服务和APP服务的注册后将服务进行了初始化,里面调用的两个非常重要的函数:InitializeAllServices和InitCompleted,进行初始化服务的大部分功能由它们两个完成,所以不看不行,本篇文章先看其中一个InitializeAllServices
还是先把前面的系列文章链接附在这里供小伙伴阅读学习
SA框架下的基于Publish函数的广播机制(从广播机制切入Samgr)(前言)
Samgr_lite——如何注册一个服务?(以广播服务为例)(序章)
Samgr_lite——如何注册一个服务?(以广播服务为例)(正篇)
Samgr_lite——如何注册服务对应的feature?(续章)
Samgr_lite——重要结构体Vector的相关知识
Samgr_lite——如何初始化服务?(终章)
干货来袭!我们探索的思路采取从代码出发,层层抽丝剥茧看看里面究竟如何运作
InitializeAllServices
1. 整体解读
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循环
1.2 InitializeAllServices流程图
同样给出InitializeAllServices的流程图供读者参考
1.3 重要结构体TaskConfig
这里我们看到一个新的结构体类型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;
1.4 重要结构体TaskPool
在后面讲解AddTaskPool中会对两个结构体进行解读
所以对于初始化的解读就自然落在了下面三个重要函数身上
2. 三个重要函数
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的目的也是如此
下面深入到几个函数源代码中瞧瞧其工作原理
2.1.1 SAMGR_CreateFixedTaskPool
逻辑比较简单,调用QUEUE_Create进行队列的创建;先调用SAMGR_Malloc进行taskPool空间分配,再依次给各个属性赋初值
这里注意一点:一个taskPool的队列只有一个,而对应的线程可以由很多;一个taskPool也可以对应多个serviceImpl——它的数量在taskPool->ref中记录(最大为15)
2.1.2 SAMGR_ReferenceTaskPool
2.1.3 GetSpecifiedTaskPool
主要通过for循环遍历所有的serviceImpl,将其taskPool.config与给定的config进行memcmp,一致则为需要的taskPool,返回该taskPool,然后taskPool.ref++表示关联的服务+1——通过该函数进行SPECIFIED_TASK类型的服务taskPool的添加
综上所述:AddTaskPool的实质是对于不同类型taskPool创建的不同处理
2.1.4 函数流程图
2.2 InitializeSingleService
先贴代码注解:
分两步走:如果传入的service没有对应的taskPool则调用DEFAULT_Initialize进行默认初始化处理;否则调用SAMGR_SendSharedDirectRequest传入HandleInitRequest交给对应的线程进行初始化处理 (处理的时候同样也调用了DEFAULT_Initialize)
下面是涉及的三个主要函数
2.2.1 DEFAULT_Initialize
这里也是分两步走:
第一步对serviceImpl调用Initialize和GetName进行初始化和获取服务名称,然后调用SAMGR_RegisterServiceApi进行默认的serviceApi注册(featureName:NULL);
第二步对于serviceimpl中绑定的所有feature后进行OnInitialize,然后获取其name传入SAMGR_RegisterServiceApi再次进行serviceImpl的注册
这里SAMGR_RegisterServiceApi的具体讲解由于篇幅原因在下下篇文章展开
2.2.2 SAMGR_SendSharedDirectRequest
在讲述广播机制的时候也提到过该函数,主要是实现对于exchange结构体的封装并调用SharedSend将exchange发送到指定队列中,处理的时候会根据里面封装的handler函数进行处理并返回结果
而这里的处理函数就是HandleInitRequest
2.2.3 HandleInitRequest
里面的核心也是DEFAULT_Initialize函数进行serviceApi的注册;
然后在serviceImpl->ops.timestamp中添加了初始化的时间戳;
最后调用了最后一篇要讲的InitCompleted来判断是否所有的服务都已经初始化完成
综上所述:InitializeSingleService的实质是完成了每个serviceImpl的serviceApi的注册
2.2.4 函数流程图
2.3 SAMGR_StartTaskPool
在完成了taskPool和serviceApi的注册后,来到最后一步——taskPool的启动
贴上代码注释:
函数的逻辑也比较清晰
1.首先是attr的创建
2.然后是调用THREAD_Create进行线程的创建(重要的任务入口函数TaskEntry)
3.再是在tasks中记录线程Id——便于以后的调用
4.最后就是根据size的大小,为一个taskPool创建多个thread,直到top>=size为止
具体task的运行机制大家可以自行阅读
函数流程图:
3. 总结
至此InitializeAllServices的所有代码基本分析完毕
它主要实现三件事来初始化所有的服务:
第一,三类taskPool的添加和创建,包括队列的创建;
第二,针对service和对应的feature进行serviceApi的注册;
第三,为创建的taskPool创建多个线程启动任务池。
这里给出整体的流程图供读者参考:
下一篇文章讲解InitCompleted!