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

2024-06-18 19:04

本文主要是介绍移植案例与原理 - utils子系统之KV存储部件 (2),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

3、KV存储部件对外接口

在文件utils\native\lite\include\kv_store.h中定义了KV存储部件对外接口,如下,支持从键值对缓存里读取键值,设置键值,删除键值,清除缓存等等。

int UtilsGetValue(const char* key, char* value, unsigned int len);int UtilsSetValue(const char* key, const char* value);int UtilsDeleteValue(const char* key);#ifdef FEATURE_KV_CACHE
int ClearKVCache(void);
#endif

在文件utils\native\lite\kv_store\innerkits\kvstore_env.h中定义了如下接口,在使用POSIX接口时,需要首先使用接口需要设置数据文件路径。使用UtilsFile接口时,不需要该接口。

int UtilsSetEnv(const char* path);

4、KV存储部件对应POSIX接口部分的代码

分析下KV存储部件对应POSIX接口部分的代码。我们知道对外接口有设置键值UtilsSetValue、获取键值UtilsGetValue、删除键值UtilsDeleteValue和清除缓存ClearKVCache。我们先看看内部接口。

4.1 内部接口

4.1.1 GetResolvedPath解析路径

函数GetResolvedPath用于解析文件路径,根据键名key组装存放值value的文件路径。需要4个参数,第一个参数char* dataPath为键值对保存的文件路径,在使用KV特性前由UtilsSetEnv函数设置到全局变量里g_dataPath;第二个参数为键char* key;第三个参数char* resolvedPath为解析后的路径,为输出参数;第4个参数unsigned int len为路径长度。看下代码,⑴处为解析的路径申请内存,⑵处拼装键值对的文件路径,格式为"XXX/kvstore/key"。⑶将相对路径转换成绝对路径,如果解析成功,会把文件路径解析到输出参数resolvedPath。⑷处如果执行realpath函数出错,指定的文件不存在,会执行⑸把keyPath复制到输出函数resolvedPath。

static int GetResolvedPath(const char* dataPath, const char* key, char* resolvedPath, unsigned int len)
{
⑴  char* keyPath = (char *)malloc(MAX_KEY_PATH + 1);if (keyPath == NULL) {return EC_FAILURE;}
⑵  if (sprintf_s(keyPath, MAX_KEY_PATH + 1, "%s/%s/%s", dataPath, KVSTORE_PATH, key) < 0) {free(keyPath);return EC_FAILURE;}
⑶  if (realpath(keyPath, resolvedPath) != NULL) {free(keyPath);return EC_SUCCESS;}
⑷  if (errno == ENOENT) {
⑸      if (strncpy_s(resolvedPath, len, keyPath, strlen(keyPath)) == EOK) {free(keyPath);return EC_SUCCESS;}}free(keyPath);return EC_FAILURE;
}

4.1.2 GetValueByFile从文件中读取键值

函数GetValueByFile从文件中读取键对应的值,需要4个参数,第一个参数为键值文件存放的目录路径;第二个参数为键;第三个为输出参数,存放获取的键的值;第4个参数为输出参数的长度。该函数返回值为EC_FAILURE或成功获取的值的长度。⑴处获取对应键名key的文件路径,⑵处读取文件的状态信息。因为文件内容是键对应的值,⑶处表明如果值的大小大于等于参数len,则返回错误码。等于也不行,需要1个字符长度存放null字符用于结尾。⑷处打开文件,然后读取文件,内容会存入输出参数value里。⑸处设置字符串结尾的null字符。

static int GetValueByFile(const char* dataPath, const char* key, char* value, unsigned int len)
{char* keyPath = (char *)malloc(PATH_MAX + 1);if (keyPath == NULL) {return EC_FAILURE;}
⑴  if (GetResolvedPath(dataPath, key, keyPath, PATH_MAX + 1) != EC_SUCCESS) {free(keyPath);return EC_FAILURE;}struct stat info = {0};
⑵  if (stat(keyPath, &info) != F_OK) {free(keyPath);return EC_FAILURE;}
⑶  if (info.st_size >= len) {free(keyPath);return EC_FAILURE;}
⑷  int fd = open(keyPath, O_RDONLY, S_IRUSR);free(keyPath);keyPath = NULL;if (fd < 0) {return EC_FAILURE;}int ret = read(fd, value, info.st_size);close(fd);fd = -1;if (ret < 0) {return EC_FAILURE;}
⑸  value[info.st_size] = '\0';return info.st_size;
}

4.1.3 SetValueToFile\DeleteValueFromFile存入\删除键值

函数SetValueToFile同于把键值存入文件,函数DeleteValueFromFile则用于删除键值。⑴处根据键名获取存放值的文件路径keyPath,⑵处打开文件,然后写入键名对应的值。在函数DeleteValueFromFile中,⑶处先组装路径,然后删除文件。

static int SetValueToFile(const char* dataPath, const char* key, const char* value)
{char* keyPath = (char *)malloc(PATH_MAX + 1);if (keyPath == NULL) {return EC_FAILURE;}
⑴  if (GetResolvedPath(dataPath, key, keyPath, PATH_MAX + 1) != EC_SUCCESS) {free(keyPath);return EC_FAILURE;}
⑵  int fd = open(keyPath, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);free(keyPath);keyPath = NULL;if (fd < 0) {return EC_FAILURE;}int ret = write(fd, value, strlen(value));close(fd);fd = -1;return (ret < 0) ? EC_FAILURE : EC_SUCCESS;
}static int DeleteValueFromFile(const char* dataPath, const char* key)
{char* keyPath = (char *)malloc(MAX_KEY_PATH + 1);if (keyPath == NULL) {return EC_FAILURE;}
⑶  if (sprintf_s(keyPath, MAX_KEY_PATH + 1, "%s/%s/%s", dataPath, KVSTORE_PATH, key) < 0) {free(keyPath);return EC_FAILURE;}int ret = unlink(keyPath);free(keyPath);return ret;
}

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_CACHE
⑵  if (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_CACHE
⑷  AddKVCache(key, value, FALSE);
#endifpthread_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_CACHEAddKVCache(key, value, TRUE);
#endifif (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_CACHE
⑴  DeleteKVCache(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;
}
#endifint 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;
}

如果大家想更加深入的学习 OpenHarmony 开发的内容,不妨可以参考以下相关学习文档进行学习,助你快速提升自己:

OpenHarmony 开发环境搭建:https://qr18.cn/CgxrRy

《OpenHarmony源码解析》:https://qr18.cn/CgxrRy

  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ……

系统架构分析:https://qr18.cn/CgxrRy

  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……

OpenHarmony 设备开发学习手册:https://qr18.cn/CgxrRy

在这里插入图片描述

OpenHarmony面试题(内含参考答案):https://qr18.cn/CgxrRy

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:https://qr21.cn/FV7h05

这篇关于移植案例与原理 - utils子系统之KV存储部件 (2)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/1072845

相关文章

MySQL 临时表与复制表操作全流程案例

《MySQL临时表与复制表操作全流程案例》本文介绍MySQL临时表与复制表的区别与使用,涵盖生命周期、存储机制、操作限制、创建方法及常见问题,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随小... 目录一、mysql 临时表(一)核心特性拓展(二)操作全流程案例1. 复杂查询中的临时表应用2. 临时

MySQL 数据库表与查询操作实战案例

《MySQL数据库表与查询操作实战案例》本文将通过实际案例,详细介绍MySQL中数据库表的设计、数据插入以及常用的查询操作,帮助初学者快速上手,感兴趣的朋友跟随小编一起看看吧... 目录mysql 数据库表操作与查询实战案例项目一:产品相关数据库设计与创建一、数据库及表结构设计二、数据库与表的创建项目二:员

redis和redission分布式锁原理及区别说明

《redis和redission分布式锁原理及区别说明》文章对比了synchronized、乐观锁、Redis分布式锁及Redission锁的原理与区别,指出在集群环境下synchronized失效,... 目录Redis和redission分布式锁原理及区别1、有的同伴想到了synchronized关键字

C#中的Drawing 类案例详解

《C#中的Drawing类案例详解》文章解析WPF与WinForms的Drawing类差异,涵盖命名空间、继承链、常用类及应用场景,通过案例展示如何创建带阴影圆角矩形按钮,强调WPF的轻量、可动画特... 目录一、Drawing 是什么?二、典型用法三、案例:画一个“带阴影的圆角矩形按钮”四、WinForm

Linux中的HTTPS协议原理分析

《Linux中的HTTPS协议原理分析》文章解释了HTTPS的必要性:HTTP明文传输易被篡改和劫持,HTTPS通过非对称加密协商对称密钥、CA证书认证和混合加密机制,有效防范中间人攻击,保障通信安全... 目录一、什么是加密和解密?二、为什么需要加密?三、常见的加密方式3.1 对称加密3.2非对称加密四、

setsid 命令工作原理和使用案例介绍

《setsid命令工作原理和使用案例介绍》setsid命令在Linux中创建独立会话,使进程脱离终端运行,适用于守护进程和后台任务,通过重定向输出和确保权限,可有效管理长时间运行的进程,本文给大家介... 目录setsid 命令介绍和使用案例基本介绍基本语法主要特点命令参数使用案例1. 在后台运行命令2.

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

RabbitMQ消费端单线程与多线程案例讲解

《RabbitMQ消费端单线程与多线程案例讲解》文章解析RabbitMQ消费端单线程与多线程处理机制,说明concurrency控制消费者数量,max-concurrency控制最大线程数,prefe... 目录 一、基础概念详细解释:举个例子:✅ 单消费者 + 单线程消费❌ 单消费者 + 多线程消费❌ 多

在MySQL中实现冷热数据分离的方法及使用场景底层原理解析

《在MySQL中实现冷热数据分离的方法及使用场景底层原理解析》MySQL冷热数据分离通过分表/分区策略、数据归档和索引优化,将频繁访问的热数据与冷数据分开存储,提升查询效率并降低存储成本,适用于高并发... 目录实现冷热数据分离1. 分表策略2. 使用分区表3. 数据归档与迁移在mysql中实现冷热数据分

MySql基本查询之表的增删查改+聚合函数案例详解

《MySql基本查询之表的增删查改+聚合函数案例详解》本文详解SQL的CURD操作INSERT用于数据插入(单行/多行及冲突处理),SELECT实现数据检索(列选择、条件过滤、排序分页),UPDATE... 目录一、Create1.1 单行数据 + 全列插入1.2 多行数据 + 指定列插入1.3 插入否则更