随着CPU算力的发展,尤其是ARM核成本的降低,内存成本和内存容量成为约束业务成本和性能的核心痛点,因此如何节省内存成本,如何扩大内存容量成为存储迫切要解决的问题。
etmem内存分级扩展技术,通过DRAM+内存压缩/高性能存储新介质形成多级内存存储,对内存数据进行分级,将分级后的内存冷数据从内存介质迁移到高性能存储介质中,达到内存容量扩展的目的,从而实现内存成本下降。
安装和运行
下载etmem源码:
git clone https://gitee.com/openeuler/etmem.git
编译和运行依赖
etmem的编译和运行依赖于libboundscheck组件:yum install libboundscheck
下载 uthash, 常用的 C 语言头文件,用于实现哈希表。实现哈希表依赖uthash.h
wget https://raw.githubusercontent.com/troydhanson/uthash/master/src/uthash.h -P ./etmem/etmem/inc/etmemd_inc/
或者:
wget https://github.com/troydhanson/uthash/archive/refs/heads/master.zip
unzip master.zip
cp uthash-master/src/uthash.h ./etmem/etmem/inc/etmemd_inc/
安装 numactl 库。numaif.h 是一个用于 NUMA(非统一内存访问)系统的头文件,通常包含在 numactl 库中:
sudo yum install numactl-devel
安装 libcap 库。sys/capability.h 是一个用于处理进程能力(capabilities)的头文件,通常包含在 libcap 库中:
sudo yum install libcap-devel
安装 json-c 库。json-c/json.h 是一个用于处理 JSON 数据的 C 语言库头文件,通常包含在 json-c 库中:
sudo yum install json-c-devel
sudo yum install libpfm libpfm-devel
sudo yum install cmake
sudo yum install gcc-c++
编译
cd etmem mkdir build cd build cmake -DENABLE_PMU=ON .. make
确认 etmemd 可执行文件位置配置环境变量
find /root/etmem/etmem/build -name etmemd
结果:/root/etmem/etmem/build/bin/etmemd
将 etmemd 目录确保替换/root/etmem/build/path/to/etmemd
为实际的 etmemd 所在目录路径。
vim /etc/profile
export PATH=/root/etmem/etmem/build/bin/:$PATH
source /etc/profile
运行依赖
需要依赖于内核态的特性支持,在运行时需要插入etmem_scan和etmem_swap模块。openuler21.03、21.09、20.03 LTS SP2以及20.03 LTS SP3均支持etmem内存扩展相关特性,可以直接使用以上内核。modprobe etmem_scan modprobe etmem_swap
权限限制
运行etmem进程需要root权限,其配置文件要求权限为600/400。运行
- 先是启动进程:
etmemd -l 0 -s etmemd_socket
log打印到/var/log/messages
文件中, etmemd监听的名称,用于与客户端交互。
cat messages | grep -i '.*etmemd.*' | tail -n 30
- 然后要添加对象
添加对象:(-s 与etmemd服务端通信的socket名称)
这里没有自动放在文档说的文件夹内,所以直接指定路径在conf文件夹,但是需要修改文件权限才能运行:chmod 600 /root/etmem/etmem/conf/cslide_conf.yaml etmem obj add -f /root/etmem/etmem/conf/cslide_conf.yaml -s etmemd_socket etmem obj del -f /root/etmem/etmem/conf/cslide_conf.yaml -s etmemd_socket (chmod 600 /root/etmem/etmem/conf/pmu_conf.yaml etmem obj add -f /root/etmem/etmem/conf/pmu_conf.yaml -s etmemd_socket)
- 删除对象:
etmem obj del -f /root/etmem/etmem/conf/pmu_conf.yaml -s etmemd_socket
打印帮助:
etmem obj help
- 启动工程开始任务
启动工程:etmem project start -n test1 -s etmemd_socket
停止工程:etmem project stop -n day30 -s etmemd_socket
查询工程:etmem project show -n newday4 -s etmemd_socket
- 先是启动进程:
配置文件解析
使用 GKeyFile 来加载和解析配置文件。GKeyFile 是一个抽象的数据结构,它在 GLib 库中定义,用于表示和操作类似于 INI 文件的配置数据。使用 GKeyFile 常用的一些函数,用于访问和操作配置文件中的数据:
g_key_file_new():创建一个新的 GKeyFile 实例。
g_key_file_load_from_file():从文件加载配置数据到 GKeyFile 实例中。
g_key_file_has_group():检查 GKeyFile 中是否存在指定的节(group/section)。
g_key_file_get_value():获取指定节下的键(key)对应的值(value)。
g_key_file_get_integer():获取指定节下的键对应的整数值。
g_key_file_get_string():获取指定节下的键对应的字符串值。
g_key_file_get_boolean():获取指定节下的键对应的布尔值。
g_key_file_free():释放 GKeyFile 实例占用的资源。
在etememd_file中parse_item
做了解析。parse_item
函数用于解析 GKeyFile 中的一个特定键的值,并根据其类型将其转换为适当的数据类型,然后调用回调函数将其填充到目标对象中。
parse_file_config
函数用于遍历一个配置项数组,并对每个配置项调用parse_item
函数进行解析。通过定义不同的 config_item
结构扩展或修改配置文件的处理逻辑。
int parse_file_config(GKeyFile *config, char *group_name, struct config_item *items, unsigned n, void *obj)
/*
config: 指向 GKeyFile 对象的指针。
group_name: 配置文件中的节名称。
config_item *items 要解析的键
void *obj 接收解析后的键
unsigned n 配置项数组的大小
*/
//在etmemd_cslide中的例子
struct cslide_task_params *params = calloc(1, sizeof(struct cslide_task_params));
parse_file_config(config, TASK_GROUP, g_cslide_task_config_items, ARRAY_SIZE(g_cslide_task_config_items), (void *)params) != 0
struct cslide_eng_params *params = calloc(1, sizeof(struct cslide_eng_params));
parse_file_config(config, ENG_GROUP, cslide_eng_config_items, ARRAY_SIZE(cslide_eng_config_items), (void *)params) != 0
设计概要
前台线程
前台线程所做的工作是处理命令行,并将命令行的数据保存到mem_proj
,内容通过RPC远程进程间通信传递,并没有解析配置文件等。
etmem_inc
在etmem_common.h
中有:
struct mem_proj { // 这里解析了配置文件的内容,但是可能是一部分配置文件的内容。对于object—>project->engine都需要一个这个结构体。
etmem_cmd cmd; // 只会从g_project_cmd_items中的三个里面选
char *proj_name; // 从命令行解析
char *eng_name; // engine mem_proj的初始化会给出进程
char *task_name; // engine mem_proj的初始化会给出进程
char *file_name; // project的初始化mem_proj命令会给出file name
char *sock_name; // 从命令行解析
char *eng_cmd; // 对于引擎的配置proj->eng_cmd = conf->argv[0];conf不清楚怎么组织的 proj->cmd = ETMEM_CMD_ENGINE;这里也是cmd menu中的一个
};
收集要给后台执行的相关信息,rpc通信
int etmem_rpc_client(const struct mem_proj *proj);
etmem.h
typedef enum etmem_cmd_e {
ETMEM_CMD_ADD,
ETMEM_CMD_DEL,
ETMEM_CMD_START,
ETMEM_CMD_STOP,
ETMEM_CMD_SHOW,
ETMEM_CMD_ENGINE,
ETMEM_CMD_HELP,
} etmem_cmd;
// 这个结构体和命令行有关
struct etmem_conf {
char *obj;
etmem_cmd cmd;
int argc;
char **argv;
};
struct etmem_obj { // 保存启动命令“etmem project start -n test2 -s etmemd_socket”相关信息的
char *name; // 保存字符串
void (*help)(void); // 函数指针不接受参数并且返回为void
int (*do_cmd)(struct etmem_conf *conf); // 一个函数指针,指向接受一个 int 参数且返回类型为 int 的函数。在这里,它被初始化为 project_do_cmd 函数的地址。
SLIST_ENTRY(etmem_obj) entry; // 方便遍历
};
etmem_src
启动进程—>添加obj—>启动projectetmem_project.h
static struct etmem_obj g_etmem_project = {
.name = "project",
.help = project_help, // 这个函数的功能就是输出help信息的
.do_cmd = project_do_cmd, // 实例化配置文件mem_proj的信息,但是只写了要执行的指令、项目名、socket名这些
};
void project_init(void)
{
etmem_register_obj(&g_etmem_project);
}
etmem_project.c
struct project_cmd_item {
char *cmd_name;
enum etmem_cmd_e cmd;
};
static struct project_cmd_item g_project_cmd_items[] = { // 这个结构体的实例化
{"show", ETMEM_CMD_SHOW},
{"start", ETMEM_CMD_START},
{"stop", ETMEM_CMD_STOP},
};
这里是表示命令行的内容启动一个项目或者停止一个项目之类的。
引擎设计
引擎相关结构体:
引擎实际执行时的函数调用链: