Sa_Store.c存储服务信息
这篇文章我们来读samgr_endpoint/source/sa_store.c和sa_store.h
1. sa_store.h
首先从整个信息存储的结构体入手,再分析其对应的操作函数
1.1 SAStore
这是整个结构体的入口
里面包含了五项属性:
- int saSize:保存了整个结构体的大小,每当要新加入内容时,会在这里更新其大小
- ListNode * root:后续服务和feature的信息会存储在这个链表中
- int16 mapSize:maps数组的大小
- int16 mapTop:相当于指向数组的指针
- PidHandle *maps:存储handle相关数据的结构
我们从ListNode结构体开始往下深入
1.2 ListNode
仅含两个属性:
- ListNode * next:指向下一个结点的指针
- ServiceInfo info:每个结点代表一个服务,info存储了该服务的信息
1.3 ServiceInfo
ServiceInfo三个属性:
- char name[ ]:服务名称的存储数组
- handle:服务处理函数的信息
- FeatureNode *head:一个服务对应0个、1个或多个feature同样需要一个feature链表组织管理其信息
1.4 FeatureNode
feature结点上四个属性:
char name[ ]:feature的名称
uint32 isDefault:是否为默认
uint32 token:identity中的重要信息
FeatureNode *next:指向下一个结点的指针
1.5 PidHandle
maps的存储结构:
四个属性:
- pid_t pid:用于查找的属性(关于权限的id)
- uid_t uid:用于查找的属性(关于权限的id)
- uint32 handle:samgr的handle类型编号
- uint32 deadId:进行注销回调是留下的deadId
1.6 结构体展开图
这里简单的将结构体展开,便于读者更好的理解结构体之间的嵌套关系
看完结构我们看到其操作函数
2. sa_store.c
这里只挑选几个重要的函数进行详细讲解,其余的简单介绍下功能,留给读者自己研读
2.1 SASTORA_FindHandleByPid
第一个函数是在maps[ ]数组中根据给定的pid返回maps[ ]中一致pid所在的位置
里面应用了非常常见的查找方式:二分查找,复杂度为lg(N)——也间接说明了maps中各个结点按照pid的大小进行排序,在后面maps的save函数中也能看出插入的时候是按pid有序插入
下面涉及了两个存储函数,第一个是往maps中存储;另一个是往ListNode链表中存储
2.2 SASTORA_SaveHandleByPid
/*
函数功能:将给定的handle存入maps(不存在对应pid则新建,存在pid但内容不同则更新,pid和内容都相同则直接返回)
函数参数:saStore:存储的maps所在结构体;handle:需要存储的PidHandle:类型
函数返回:EC_SUCCESS:成功;EC_NOMEMORY:内存不足
*/
int SASTORA_SaveHandleByPid(SAStore *saStore, PidHandle handle)
{
PidHandle saved = {.handle = INVALID_INDEX};
//通过pid在maps中找到对应index
int index = SASTORA_FindHandleByPid(saStore, handle.pid, &saved);
//在这里判断是否相同,相同直接返回
if (saved.handle == handle.handle) {
return EC_SUCCESS;
}
//pid一致但handle不一致则更新handle
if (index != INVALID_INDEX) {
saStore->maps[index] = handle;
return EC_SUCCESS;
}
//不存在则需要新添加
//先更新maps的大小
if (saStore->mapSize <= saStore->mapTop) {
PidHandle *newMap = (PidHandle *)SAMGR_Malloc(sizeof(PidHandle) * (saStore->mapSize + GROW_STEP));
if (newMap == NULL) {
return EC_NOMEMORY;
}
if (saStore->maps != NULL) {
(void)memcpy_s(newMap, sizeof(PidHandle) * (saStore->mapSize + GROW_STEP),
saStore->maps, sizeof(PidHandle) * saStore->mapSize);
}
PidHandle *oldMap = saStore->maps;
saStore->maps = newMap;
saStore->mapSize += GROW_STEP;
//释放旧maps
SAMGR_Free(oldMap);
}
int i;
//实现插入排序 这里是按照pid从小到大的顺序进行排序
//找到第一个比要插入pid小的结点
for (i = saStore->mapTop - 1; i >= 0; --i) {
if (saStore->maps[i].pid < handle.pid) {
break;
}
//比其大的结点后移
saStore->maps[i + 1] = saStore->maps[i];
}
//在其后面拆入handle
saStore->maps[i + 1] = handle;
++(saStore->mapTop);
return EC_SUCCESS;
}
函数流程:
1. 调用SASTORA_FindHandleByPid根据pid在原maps中查找是否存在该pid
2. 存在该pid且handle也相同,则无需更新直接返回
3. 存在相同pid但对应handle不同,则更新handle后返回
4. 不存在pid则需要新添加数据
5. 首先更新maps的大小(这里虽然是使用了数组但是数组的大小是根据数据的大小而定,所有要新添加数据需要先扩展数组大小)
6. 将旧数据拷贝到新maps中,释放oldmaps
7. 这里按照pid从小到大的顺序进行排序,所以从最后一个开始找到比新插入pid小的结点,比其大的结点往后移一位(插入排序的思路)
8. 将新pid添加在其后面
2.3 SASTORA_Save
该函数用于service和feature链表中结点的添加
/*
函数功能:将identity中的信息存入saStore中
函数参数:saStore:存储的结构体;service、feature:对应字符串;identity:需要存储的身份信息
函数返回:EC_INVALID:无效参数;EC_NOSPACE:结构已满;EC_NOMEMORY:内存已满;EC_SUCCESS:成功
*/
int SASTORA_Save(SAStore *saStore, const char *service, const char *feature, const SvcIdentity *identity)
{
//检查传入参数是否为空
if (saStore == NULL || service == NULL || identity == NULL) {
return EC_INVALID;
}
//调用根据serviceName查找root结点下的serviceNode
ListNode *curNode = FindServiceByName(saStore->root, service);
//找到则获取其featureList的head结点
FeatureNode *fNode = (curNode == NULL) ? NULL : curNode->info.head;
//再通过featureName查找对应featureNode
fNode = FindFeatureByName(fNode, feature);
//存在直接返回不用存储
if (fNode != NULL) {
return EC_SUCCESS;
}
if (saStore->saSize >= MAX_SA_NUM) {
return EC_NOSPACE;
}
fNode = SAMGR_Malloc(sizeof(FeatureNode));
if (fNode == NULL) {
return EC_NOMEMORY;
}
//给featureNode属性赋值
fNode->token = identity->token;
//由于给定的featureName可以为空,为空则说明为default
fNode->isDefault = feature == NULL;
fNode->name[0] = 0;
//给定了featureName则存入name数组中
if (feature != NULL) {
if (strcpy_s(fNode->name, MAX_NAME_LEN, feature) != EOK) {
SAMGR_Free(fNode);
return EC_INVALID;
}
}
//当serviceNode无法找到时
if (curNode == NULL) {
curNode = SAMGR_Malloc(sizeof(ListNode));
if (curNode == NULL) {
SAMGR_Free(fNode);
return EC_NOMEMORY;
}
//进行serviceName的拷贝
if (strcpy_s(curNode->info.name, MAX_NAME_LEN, service) != EOK) {
SAMGR_Free(fNode);
SAMGR_Free(curNode);
return EC_INVALID;
}
//service结点的属性拷贝
curNode->info.handle = identity->handle;
curNode->info.head = NULL;
//循环链表
curNode->next = saStore->root;
saStore->root = curNode;
}
//循环链表
fNode->next = curNode->info.head;
curNode->info.head = fNode;
saStore->saSize++;
return EC_SUCCESS;
}
函数流程如下:
1. 调用函数FindServiceByName返回对应的serviceNode
2. 如果该service存在feature链表则调用FindFeatureByName返回对应feature
3. 如果存在该feature则直接返回说明无需添加
4. 不存在则新建一个fNode将identity->token赋给其属性token,并将isDefault和name初始化
5. 如果参数传了featureName则拷贝入name属性中
6. 当调用FindServiceByName无法找到对应serviceNode,则创建一个新serviceNode并初始化
7. 将serviceNode上链
8. 将fNode上链
这里可以看到serviceList和featureList都是循环链表,头尾相连
2.4 其他
其他函数比较简单,这里仅做功能介绍,留给读者自行研读
- SASTORA_FindHandleByUidPid:通过uid或pid一致来查询对应maps数据——同样用到了二分查找
- SASTORA_FindPidHandleByIpcHandle:通过给定的handle在maps找到handle一致的pidHandle
- SASTORA_Find:调用FindServiceByName和FindFeatureByName获取service的handle和feature的token存入identity中并返回
- SASTORA_ClearByPid:通过pid清除maps和serviceList中对应数据
- SASTORA_ClearServiceByHandle:通过handle查找serviceList上一致handle的结点删除
- FreeTreeNode:释放链表结点
3. 总结
saStore主要存储了service和feature相关的信息数据(包括service的handle和feature的token等),数据结构为循环链表,通过pid可以唯一定位一个service和maps中的一项数据体,在功能函数中使用了插入排序根据pid的大小对maps的数据项进行排序,使用二分查找对maps中数据项进行查找