OpenHarmony移植案例与原理 - utils子系统之KV存储部件(2)

举报
zhushy 发表于 2022/02/22 11:36:59 2022/02/22
【摘要】 移植案例与原理 - utils子系统之KV存储部件 4.1.4 InitKv创建kvstore目录函数InitKv确保保存键值时,kvstore目录被创建,用于存放键值文件。⑴处组装kvstore目录,⑵处使用F_OK参数判断目录是否存在,如果存在返回EC_SUCCESS。否则执行⑶创建kvstore目录。static int InitKv(const char* dataPath){ ...

移植案例与原理 - utils子系统之KV存储部件

4.1.4 InitKv创建kvstore目录

函数InitKv确保保存键值时,kvstore目录被创建,用于存放键值文件。⑴处组装kvstore目录,⑵处使用F_OK参数判断目录是否存在,如果存在返回EC_SUCCESS。否则执行⑶创建kvstore目录。

static int InitKv(const char* dataPath)
{
    if (dataPath == NULL) {
        return EC_FAILURE;
    }
    char* kvPath = (char *)malloc(MAX_KEY_PATH + 1);
    if (kvPath == NULL) {
        return EC_FAILURE;
    }if (sprintf_s(kvPath, MAX_KEY_PATH + 1, "%s/%s", dataPath, KVSTORE_PATH) < 0) {
        free(kvPath);
        return EC_FAILURE;
    }if (access(kvPath, F_OK) == F_OK) {
        free(kvPath);
        return EC_SUCCESS;
    }if (mkdir(kvPath, S_IRUSR | S_IWUSR | S_IXUSR) != F_OK) {
        free(kvPath);
        return EC_FAILURE;
    }
    free(kvPath);
    return EC_SUCCESS;
}

4.1.5 GetCurrentItem获取当前的键值对数目

函数GetCurrentItem用于获取当前的键值对数目。首先,组装目录路径"XXX/kvstore",然后执行⑴打开目录,然后读取目录项。⑵循环每一个目录项,判断键值对的数量。⑶处组装kvstore目录下每一个键的文件路径,然后获取每个文件的状态信息。⑷如果文件是常规普通文件,则键值对数量加1。然后读取kvstore目录下的下一个目录项,依次循环。

static int GetCurrentItem(const char* dataPath)
{
    char kvPath[MAX_KEY_PATH + 1] = {0};
    if (sprintf_s(kvPath, MAX_KEY_PATH + 1, "%s/%s", dataPath, KVSTORE_PATH) < 0) {
        return EC_FAILURE;
    }DIR* fileDir = opendir(kvPath);
    if (fileDir == NULL) {
        return EC_FAILURE;
    }
    struct dirent* dir = readdir(fileDir);
    int sum = 0;while (dir != NULL) {
        char fullPath[MAX_KEY_PATH + 1] = {0};
        struct stat info = {0};if (sprintf_s(fullPath, MAX_KEY_PATH + 1, "%s/%s", kvPath, dir->d_name) < 0) {
            closedir(fileDir);
            return EC_FAILURE;
        }
        if (stat(fullPath, &info) != 0) {
            closedir(fileDir);
            return EC_FAILURE;
        }if (S_ISREG(info.st_mode)) {
            sum++;
        }
        dir = readdir(fileDir);
    }
    closedir(fileDir);
    return sum;
}

4.1.6 NewItem判断是否新键值对

函数NewItem可以用于判断是否新的键值对。⑴处获取键名对应的文件路径,⑵处判断文件是否存在,存在则返回FALSE;不存在键值对则返回TRUE。

static boolean NewItem(const char* dataPath, const char* key)
{
    char* keyPath = (char *)malloc(MAX_KEY_PATH + 1);
    if (keyPath == NULL) {
        return FALSE;
    }if (sprintf_s(keyPath, MAX_KEY_PATH + 1, "%s/%s/%s", dataPath, KVSTORE_PATH, key) < 0) {
        free(keyPath);
        return FALSE;
    }if (access(keyPath, F_OK) == F_OK) {
        free(keyPath);
        return FALSE;
    }
    free(keyPath);
    return TRUE;
}

4.2 读取键值UtilsGetValue

函数UtilsSetValue用于读取键名对应的值,第一个参数为输入参数键名,第二个参数为输出参数键名对应的值,第三个参数为值的字符串长度。⑴处获取键值对所在的路径,注意互斥锁的使用。如果支持键值缓存,则执行⑵尝试从缓存中读取。缓存中不能读取时,继续执行⑶从文件中读取。如果读取成功,则执行⑷,加入缓存中,注意第三个参数为FALSE。读取时,会把读取到的键值对,放到缓存的键值对链表的头部,但不删除之前的键值对数据。

int UtilsGetValue(const char* key, char* value, unsigned int len)
{
    if (!IsValidKey(key) || (value == NULL) || (len > MAX_GET_VALUE_LEN)) {
        return EC_INVALID;
    }
    pthread_mutex_lock(&g_kvGlobalMutex);const char* dataPath = g_dataPath;
    if (dataPath == NULL) {
        pthread_mutex_unlock(&g_kvGlobalMutex);
        return EC_FAILURE;
    }
#ifdef FEATURE_KV_CACHEif (GetValueByCache(key, value, len) == EC_SUCCESS) {
        pthread_mutex_unlock(&g_kvGlobalMutex);
        return EC_SUCCESS;
    }
#endif
⑶  int ret = GetValueByFile(dataPath, key, value, len);
    if (ret < 0) {
        pthread_mutex_unlock(&g_kvGlobalMutex);
        return EC_FAILURE;
    }
#ifdef FEATURE_KV_CACHEAddKVCache(key, value, FALSE);
#endif
    pthread_mutex_unlock(&g_kvGlobalMutex);
    return ret;
}

4.3 设置键值UtilsGetValue

函数UtilsSetValue用于保存一对键值,⑴处确保kvstore目录存在,不存在则创建。⑵处用于获取kvstore目录下键值对的数目。g_getKvSum默认为FALSE,只需要获取一次即可,键值对数目保存在全局变量g_kvSum。⑶处判断是否新的键值对,如果键值对数目超过缓存允许的最大数,并且需要设置的是新的缓存则返回EC_FAILURE。⑷处把键值对保存到文件中,如果支持缓存,还需要存入缓存中。注意AddKVCache存入缓存的第三方参数为TRUE,会先删除之前同一个键名对应的键值对。⑸处如果是新的键值对,键值对数目需要加1。

int UtilsSetValue(const char* key, const char* value)
{
    if (!IsValidKey(key) || !IsValidValue(value, MAX_VALUE_LEN)) {
        return EC_INVALID;
    }
    pthread_mutex_lock(&g_kvGlobalMutex);
    const char* dataPath = g_dataPath;
⑴  int ret = InitKv(dataPath);
    if (ret != EC_SUCCESS) {
        g_getKvSum = FALSE;
        pthread_mutex_unlock(&g_kvGlobalMutex);
        return EC_FAILURE;
    }if (!g_getKvSum) {
        g_kvSum = GetCurrentItem(dataPath);
        if (g_kvSum < 0) {
            pthread_mutex_unlock(&g_kvGlobalMutex);
            return EC_FAILURE;
        }
        g_getKvSum = TRUE;
    }
⑶  boolean newItem = NewItem(dataPath, key);
    if ((g_kvSum >= MAX_KV_SUM) && newItem) {
        pthread_mutex_unlock(&g_kvGlobalMutex);
        return EC_FAILURE;
    }
⑷  ret = SetValueToFile(dataPath, key, value);
    if (ret == EC_SUCCESS) {
#ifdef FEATURE_KV_CACHE
        AddKVCache(key, value, TRUE);
#endif
        if (newItem) {
⑸          g_kvSum++;
        }
    }
    pthread_mutex_unlock(&g_kvGlobalMutex);
    return ret;
}

4.4 删除键值UtilsDeleteValue

函数UtilsDeleteValue用于删除一对键值。⑴处如果支持键值缓存,则首先尝试从缓存中删除键值对。⑵处从文件中删除键值,如果删除超过,键值对数目减1。

int UtilsDeleteValue(const char* key)
{
    if (!IsValidKey(key)) {
        return EC_INVALID;
    }
    pthread_mutex_lock(&g_kvGlobalMutex);
    const char* dataPath = g_dataPath;
    if (dataPath == NULL) {
        pthread_mutex_unlock(&g_kvGlobalMutex);
        return EC_FAILURE;
    }
#ifdef FEATURE_KV_CACHEDeleteKVCache(key);
#endif
⑵  int ret = DeleteValueFromFile(dataPath, key);
    if (ret == EC_SUCCESS) {
        g_kvSum--;
    }
    pthread_mutex_unlock(&g_kvGlobalMutex);
    return ret;
}

4.5 清除键值缓存ClearKVCache和设置缓存路径UtilsSetEnv

函数ClearKVCache用于清除缓存,直接调用接口ClearKVCacheInner完成。函数UtilsSetEnv用于设置键值对的保存路径,维护在全局变量g_dataPath里。

#ifdef FEATURE_KV_CACHE
int ClearKVCache(void)
{
    pthread_mutex_lock(&g_kvGlobalMutex);
    int ret = ClearKVCacheInner();
    pthread_mutex_unlock(&g_kvGlobalMutex);
    return ret;
}
#endif

int UtilsSetEnv(const char* path)
{
    if (path == NULL) {
        return EC_FAILURE;
    }
    pthread_mutex_lock(&g_kvGlobalMutex);
    int ret = strcpy_s(g_dataPath, MAX_KEY_PATH + 1, path);
    pthread_mutex_unlock(&g_kvGlobalMutex);
    return (ret != EOK) ? EC_FAILURE : EC_SUCCESS;
}

5、KV存储部件对应UtilsFile接口部分的代码

分析下KV存储部件对应UtilsFile接口部分的代码。我们知道对外接口有设置键值UtilsSetValue、获取键值UtilsGetValue、删除键值UtilsDeleteValue和清除缓存ClearKVCache。我们先看看内部接口,这些接口调用的全部是UtilsFile接口,没有使用POSIX的文件接口。

4.1 内部接口

4.1.1 GetValueByFile和SetValueToFile从文件中读写键值

函数GetValueByFile用于从文件中读取键值,⑴处获取键名对应的键值文件的大小,如果文件大于等于参数中指定的长度len,返回EC_FAILURE。等于也不行,末尾需要放置一个空字符。⑵处打开文件,然后读取文件,读取的内容放入变量value里。⑶处末尾添加null空字符,然后返回获取的字符的长度。函数SetValueToFile用于把键值保存到文件里,⑷处调用UtilsFile接口打开,然后写入到文件里。

static int GetValueByFile(const char* key, char* value, unsigned int len)
{
    unsigned int valueLen = 0;if (UtilsFileStat(key, &valueLen) != EC_SUCCESS) {
        return EC_FAILURE;
    }
    if (valueLen >= len) {
        return EC_FAILURE;
    }
⑵  int fd = UtilsFileOpen(key, O_RDONLY_FS, 0);
    if (fd < 0) {
        return EC_FAILURE;
    }
    int ret = UtilsFileRead(fd, value, valueLen);
    UtilsFileClose(fd);
    fd = -1;
    if (ret < 0) {
        return EC_FAILURE;
    }
⑶  value[valueLen] = '\0';
    return valueLen;
}

static int SetValueToFile(const char* key, const char* value)
{
⑷  int fd = UtilsFileOpen(key, O_RDWR_FS | O_CREAT_FS | O_TRUNC_FS, 0);
    if (fd < 0) {
        return EC_FAILURE;
    }
    int ret = UtilsFileWrite(fd, value, strlen(value));
    UtilsFileClose(fd);
    fd = -1;
    return (ret < 0) ? EC_FAILURE : EC_SUCCESS;
}

4.1.2 GetCurrentItem和SetCurrentItem获取设置键值对数量

函数GetCurrentItem用于获取键值对数量,⑴处可以看出,键值对数目保存在文件KV_FILE_SUM里。从文件里读取的键值对数量会放入⑵处的字符串里,字符串的长度为4,所以键值对的数量能是K级。然后执行UtilsFileRead读取文件内容,然后通过atoi函数转换为数值。函数SetCurrentItem用于更新键值对数量,保存到文件里。⑷处把整形的参数转换为字符串,然后打开文件KV_FILE_SUM,并写入。

#define KV_SUM_FILE        "KV_FILE_SUM"
#define KV_SUM_INDEX       4
......
static int GetCurrentItem(void)
{
⑴  int fd = UtilsFileOpen(KV_SUM_FILE, O_RDWR_FS, 0);
    if (fd < 0) {
        return 0;
    }
⑵  char value[KV_SUM_INDEX] = {0};
    int ret = UtilsFileRead(fd, value, KV_SUM_INDEX);
    UtilsFileClose(fd);
    fd = -1;return (ret < 0) ? 0 : atoi(value);
}

static int SetCurrentItem(const int num)
{
    char value[KV_SUM_INDEX] = {0};if (sprintf_s(value, KV_SUM_INDEX, "%d", num) < 0) {
        return EC_FAILURE;
    }
    int fd = UtilsFileOpen(KV_SUM_FILE, O_RDWR_FS | O_CREAT_FS, 0);
    if (fd < 0) {
        return EC_FAILURE;
    }
    int ret = UtilsFileWrite(fd, value, KV_SUM_INDEX);
    UtilsFileClose(fd);
    fd = -1;
    return (ret < 0) ? EC_FAILURE : EC_SUCCESS;
}

4.1.3 NewItem判断是否新键值对

函数NewItem用于判断给定的键名是否新的键值对,是否已经存在同样的键名。调用函数UtilsFileOpen,如果能打开,说明文件已经存在,否则不存在。

static boolean NewItem(const char* key)
{
    int fd = UtilsFileOpen(key, O_RDONLY_FS, 0);
    if (fd < 0) {
        return TRUE;
    }
    UtilsFileClose(fd);
    return FALSE;
}

4.2 获取键值UtilsGetValue

函数UtilsGetValue用于从文件中读取键值,传入键名key,读出的值保存在参数value,len设置读取的值的长度。如果支持键值对缓存,则执行⑴尝试从缓存中读取,否则执行⑵从文件中读取。读取成功后,会执行⑶把读取的键值加入缓存。

int UtilsGetValue(const char* key, char* value, unsigned int len)
{
    if (!IsValidKey(key) || (value == NULL) || (len > MAX_GET_VALUE_LEN)) {
        return EC_INVALID;
    }
#ifdef FEATURE_KV_CACHEif (GetValueByCache(key, value, len) == EC_SUCCESS) {
        return EC_SUCCESS;
    }
#endif
⑵  int ret = GetValueByFile(key, value, len);
    if (ret < 0) {
        return EC_FAILURE;
    }
#ifdef FEATURE_KV_CACHEAddKVCache(key, value, FALSE);
#endif
    return ret;
}

4.3 设置键值UtilsGetValue

函数UtilsGetValue用于设置键值对到文件。⑴处获取已有的键值对的数目,⑵处判断要设置的键值是否已经存在。⑶处如果键值对数量已经大于等于允许的最大值,并且要是新增的键值对,则然后EC_FAILURE。⑷处保存键值对到文件,如果支持缓存,则加入缓存。⑸处更新键值对数量。

int UtilsSetValue(const char* key, const char* value)
{
    if (!IsValidKey(key) || !IsValidValue(value, MAX_VALUE_LEN)) {
        return EC_INVALID;
    }
⑴  int currentNum = GetCurrentItem();
⑵  boolean newItem = NewItem(key);if ((currentNum >= MAX_KV_SUM) && newItem) {
        return EC_FAILURE;
    }
⑷  int ret = SetValueToFile(key, value);
    if (ret == EC_SUCCESS) {
#ifdef FEATURE_KV_CACHE
        AddKVCache(key, value, TRUE);
#endif
        if (newItem) {
            currentNum++;
        }
    }return SetCurrentItem(currentNum);
}

4.4 删除键值UtilsDeleteValue等

函数UtilsDeleteValue用于删除键值文件,如果支持缓存则先从缓存中删除。执行⑴删除文件,⑵处更新键值对数目。函数ClearKVCache用于清除缓存。对于使用UtilsFile接口时,不需要UtilsSetEnv函数。

int UtilsDeleteValue(const char* key)
{
    if (!IsValidKey(key)) {
        return EC_INVALID;
    }
#ifdef FEATURE_KV_CACHE
    DeleteKVCache(key);
#endif
⑴  int ret = UtilsFileDelete(key);
    if (ret == EC_SUCCESS) {
⑵      ret = SetCurrentItem(GetCurrentItem() - 1);
    }
    return ret;
}

#ifdef FEATURE_KV_CACHE
int ClearKVCache(void)
{
    return ClearKVCacheInner();
}
#endif

int UtilsSetEnv(const char* path)
{
    return EC_SUCCESS;
}

参考站点

参考了下述站点,或者推荐读者阅读下述站点了解更多信息。

小结

本文介绍了utils子系统之KV存储部件的移植适配案例,分析了部件源代码。因为时间关系,仓促写作,或能力限制,若有失误之处,请各位读者多多指正。感谢阅读,有什么问题,请留言。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。