随着CPU算力的发展,尤其是ARM核成本的降低,内存成本和内存容量成为约束业务成本和性能的核心痛点,因此如何节省内存成本,如何扩大内存容量成为存储迫切要解决的问题。

etmem内存分级扩展技术,通过DRAM+内存压缩/高性能存储新介质形成多级内存存储,对内存数据进行分级,将分级后的内存冷数据从内存介质迁移到高性能存储介质中,达到内存容量扩展的目的,从而实现内存成本下降。

安装和运行

  1. 下载etmem源码:git clone https://gitee.com/openeuler/etmem.git

  2. 编译和运行依赖
    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++

  3. 编译

    cd etmem
    mkdir build
    cd build
    cmake -DENABLE_PMU=ON ..
    make
  4. 确认 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

  5. 运行依赖
    需要依赖于内核态的特性支持,在运行时需要插入etmem_scan和etmem_swap模块。openuler21.03、21.09、20.03 LTS SP2以及20.03 LTS SP3均支持etmem内存扩展相关特性,可以直接使用以上内核。

    modprobe etmem_scan
    modprobe etmem_swap
  6. 权限限制
    运行etmem进程需要root权限,其配置文件要求权限为600/400。

  7. 运行

    1. 先是启动进程:etmemd -l 0 -s etmemd_socket
      log打印到/var/log/messages文件中, etmemd监听的名称,用于与客户端交互。
      cat messages | grep -i '.*etmemd.*' | tail -n 30
    2. 然后要添加对象
      添加对象:(-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)	
    3. 删除对象:etmem obj del -f /root/etmem/etmem/conf/pmu_conf.yaml -s etmemd_socket
      ​ 打印帮助:
      etmem obj help
    4. 启动工程开始任务
      启动工程: 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_fileparse_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—>启动project
etmem_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},
};

这里是表示命令行的内容启动一个项目或者停止一个项目之类的。

引擎设计

引擎相关结构体:

引擎实际执行时的函数调用链:


文章作者: 易百分
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 易百分 !
  目录