前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >DPDK巨页地址管理/Linux内核内存管理/内存映射/pagemap/rdma内存/注册

DPDK巨页地址管理/Linux内核内存管理/内存映射/pagemap/rdma内存/注册

原创
作者头像
ssbandjl
发布2024-03-12 16:26:15
2240
发布2024-03-12 16:26:15
举报
文章被收录于专栏:Linux内核DPULinux内核

DPDK巨页地址管理/Linux内核内存管理/内存映射/pagemap/rdma内存/注册

术语

PFN: 物理地址对应的页帧号:pfn = pte_pfn(*pte)

INFINIBAND_USER_MEM:

INFINIBAND_USER_ACCESS:

FPM: Function Private Memory, 函数私有内存

PBLE: Physical Buffer List Entry 物理缓冲区列表条目

HNS: Hisilicon Network Subsystem, 海思网络子系统

简介

DPDK使用hugetlbfs内核文件系统支持大页的分配。连续的内存段抽象为rte_memseg,如果把rte_memseg中的一部分命名并以供使用,那么称为rte_memzone

在每个进程目录下有一个 pagemap 文件(/proc/self/pagemap),这个文件可以依次读取所有的虚拟页面对应的物理页帧号。通过 fseek(通知内核是哪个虚拟地址)和 read 接口(获取虚拟地址对应的物理页帧),可以精准的获取到物理地址。显然这是一个很危险的动作,直接暴露出了物理地址,所以必须要求root权限才可以执行。如果不具备root权限,就不能使用 iova-mode=PA 模式

/proc/pid/pagemap - an array mapping virtual pages to pfns, 如果页面不存在但在交换中,则 PFN 包含交换文件号的编码以及页面在交换中的偏移量。 未映射的页面返回空 PFN。 这允许精确确定哪些页面被映射(或在交换中)并比较进程之间的映射页面。 * 此接口的高效用户将使用 /proc/pid/maps 来确定实际映射的内存区域,并使用 llseek 跳过未映射的区域

Intel E810的PBLE/HMC

E810 的四散列查找和协议引擎使用主机内存作为各种上下文对象的后备存储。 主机内存缓存 (HMC) 负责缓存和管理这些上下文对象。 对于每个 RDMA 连接,协议引擎使用 QP 上下文对象(存储 TCP 序列号等的 TCP/IP 连接上下文)和入站 RDMA 读取队列 (IRRQ) 对象,该对象缓冲入站 RDMA 读取请求,直到其关联的读取响应被响应。 预定传输。 对于每个 RDMA 内存区域,协议引擎使用内存区域表条目 (MRTE) 对象来存储区域边界和访问权限信息,并使用一组物理缓冲区列表条目 (PBLE) 对象来存储虚拟到物理地址转换 这个地区。 这些以及更多协议引擎上下文对象将在第 11 章中详细介绍。第 9 节中提供了有关 HMC 操作和配置的一般信息

E810 使用主机内存作为许多上下文对象的后备存储,这些上下文对象用于跟踪队列状态和 iWARP 对象。 主机内存缓存 (HMC) 是负责管理存储在主机内存中的 iWARP 上下文对象的组件。 HMC 在每个 PCI 功能的基础上管理主机内存,并进一步将每个 PCI 功能的 HMC 内存空间分解为用于管理用于给定 PCI 功能的每个上下文对象的内存。 主机软件负责在访问特定对象之前分配 HMC 使用的主机页面。 此外,可用于特定功能的 HMC 后备存储的内存量由活动资源配置文件决定,而活动资源配置文件由软件驱动程序的操作环境和当前活动的 PCI 功能数量决定。 可以在驱动程序初始化时选择资源配置文件

HMC 需要在主机内存中驻留大量数据结构的后备存储来执行其功能。 表 9-11 提供了数据结构的列表以及需要为每个数据结构分配的内存量。 “HMC 对象位置”列指示 HMC 对象(以及关联的后备存储页面)是否仅位于 PF HMC 对象空间中,还是位于 PF 和 VF HMC 对象空间中。 通常,协议引擎 HMC 对象被分为特定于 PF 和 VF 的 HMC 对象空间。 资源可能稀少。 例如,如果一个函数分配了 512 个 QP,但只使用了 8 个 QP,则只需要分配 4K 内存,而不是为所有 512 个 QP 分配整个内存。 某些 HMC 对象需要在驱动程序初始化时完全填充,例如协议引擎哈希表条目。 有关协议引擎的 HMC 资源分配策略的更多信息,请参阅第 11.5 节

512 字节被保留用于 QP 上下文。 每个设备最多有 256K QP 上下文。 该数字除以所有支持协议引擎的 PCI 功能。 VF 对象的内存由 PF 驱动程序分配并使用 PF 请求者 ID (RID) 进行访问。 对于单功能设备,硬件保留 256K QP 之一供内部使用

每个支持协议引擎的 PCI 功能最多可分配 256M PBLE 条目。 VF 对象的内存由 VF 驱动程序分配并使用 VF 请求者 ID (RID) 进行访问。 任何关联的页面描述符都由 PF 驱动程序分配并使用 PF RID 进行访问

为了访问(并缓存在片上存储器中)表 9-11 中定义的数据结构,HMC 使用私有存储器地址空间的概念。 E810 具有 8GB 私有内存地址空间,可以根据实际上下文使用情况使用主机内存进行稀疏支持。 驱动程序不需要为驱动程序当前未使用的 HMC 对象分配页面。 私有内存地址空间首先按 PCI 功能划分,然后按对象或数据结构类型划分,最后按对象索引划分。 分配给特定 PCI 功能的私有内存地址空间部分称为功能私有内存 (FPM)。 另请注意,VF FPM 不是由 VF 驱动程序直接编程。 PF驱动器使用HMC功能索引来选择要编程的VF FPM。 E810如何提供私有内存和主机物理地址之间的地址映射,如图9-5所示。 图左侧所示的 PM 地址表示 E810 Private Memory 地址从 0 到 8GB-1。 E810内部使用PM地址空间,将其转换为主机物理地址以访问主机内存

图 9-5, 主机内存缓存私有内存地址空间

有关四重哈希缓存的信息,请参阅第 9.3.2 节。 图 9-5 的左侧部分显示了驻留在片上的 HMC 部分。 这部分包括实际的对象缓存,它保留主机内存中的部分数据以提高性能和段描述符 (SD)。 SD 驻留在片上 32 KB RAM 中,称为段描述符表。 段描述符表保存了 4096 个指向主机内存页面的指针(8B * 4096=32KB)。 段描述符表中的连续 SD 的唯一范围被分配给每个活动的 PCI 功能。 段描述符表是E810提供的第一级私有内存地址转换

SD 使用 PFHMC_SDCMD(第 13.2.2.20.35 节)、PFHMC_SDDATALOW(第 13.2.2.20.36 节)和 PFHMC_SDDATAHIGH(第 13.2.2.20.37 节)寄存器进行编程。 协议引擎 CQP 操作也可用于对段描述符进行编程。 图 9-5 中段描述符表右侧的所有内容都驻留在主机内存中。 每个 PCI 功能都有一组寄存器(GLHMC_SDPART[n] 和 GLHMC_VFSDPART[n]),用于定义属于 PCI 功能的 SD 的基数和数量。 GLHMC_SDPART[n] 和 GLHMC_VFSDPART[n] 寄存器从 NVM 编程,也可以在创建控制队列对操作期间由固件编程(第 11.5.2.1 节)。 E810 为每个内部访问提供范围检查,以确保给定的 PCI 功能绝不允许访问其有效 SD 范围之外的内存。 E810 根据资源配置文件在内部管理 SD 基址和编号寄存器,该资源配置文件在 NVM 加载时加载或由第一个 E810 驱动程序选择在创建控制队列对操作期间为设备加载(第 11.5.2.1 节)。 E810 提供的第二级私有内存地址转换是页描述符 (PD)。 每个 SD 指向一个2MB的主机页,该主机页分为 512 个 PD,这些 PD 只是 64 位物理内存地址, 每个PD大小为4KB。 每个 PD 都指向私有内存地址空间的一个后端页。 总共 8 GB(4096个SD * 2MB = 8GB) 专用内存地址空间是使用完全填充的段描述符表导出的,该表指向保存 2M PD 的 4096 个 4KB 主机页。 每个 2M PD 都指向主机内存支持页面,总共 8 GB 地址空间。 如前所述,如果软件未使用该部分专用内存地址空间,则不需要用内存填充所有 SD 或 PD。 主机内存中PD结构的格式如表9-12所示

HMC 支持页物理地址是驱动程序分配的页的地址,该页将保存 HMC 对象上下文。 该地址必须与主机内存中的 4 KB 地址对齐。 PD 有效位允许软件在需要 HMC 上下文对象时根据需要稀疏填充 PD 条目。 软件必须分配保存 PD 打包数组的主机内存页,如图 9-5 所示。 这些 PD 页的物理地址用于通过使用 PFHMC_SDCMD(第 13.2.2.20.35 节)、PFHMC_SDDATALOW(第 13.2.2.20.36 节)和 PFHMC_SDDATAHIGH(第 13.2.2.20.37 节)寄存器来填充 SD 条目。 有关如何使用这些寄存器对 SD 进行编程的更多信息,请参见第 9.3.8 节。 专用存储器进一步分为单独的 PCI 功能专用存储器 (FPM) 地址。 PCI 功能可以是物理功能,也可以是虚拟功能。 前 8 个 FPM 地址空间保留给 NIC PF。 接下来的八个 FPM 地址空间用于支持为 iWARP、RoCEv2 或 UDA 提供加速的协议引擎的 PF。 最后 32 个 FPM 地址空间用于支持协议引擎的 VF,为 iWARP、RoCEv2 或 UDA 提供加速。 图 9-6 显示了如何为每个 PCI 功能划分私有内存地址空间。 可以分配给函数的最小私有内存量为 2 MB (1 SD)。 可以分配给函数的最大值是整个段表,在这种情况下,其他函数不能拥有任何私有内存资源。 请注意,对象缓存使用 HMC 函数编号来寻址 HMC 对象以确定正确的 FPM。 FPM标识属于PCI功能的专用存储器地址空间的范围。 由于每个 SD 代表 2 MB HMC PM 地址空间,因此 FPM 还标识属于 PCI 功能的 SD 范围

图 9-6, 主机内存缓存功能私有内存空间

每个 PCI 功能的私有内存空间进一步分为主机内存中每个对象的单独内存空间。 每个 PCI 函数都有一组寄存器,用于定义 FPM 空间中对象的基地址以及特定对象的边界(或最大条目数)。 图 9-7 描述了驻留在私有内存空间中的一些当前对象(有关对象的完整列表,请参见表 9-11)。 FPM地址是根据对象类型(标识对象基址寄存器)和对象索引(FPM基址已经计算出来)计算出来的。 最终,FPM基地址、对象基地址、对象大小、对象索引都用来确定私有内存地址

图 9-8描述了将私有内存地址解码为主机地址。 此外,图 9-8 描述了 SD 直接对私有内存空间支持页进行寻址,而不是使用第二级(PD)间接寻址。 每个 PCI 功能都可以将其 SD 范围内的任何 SD 设置为指向 PD 或直接指向后端页。 段类型在PFHMC_SDDATALOW.PMSDTYPE 寄存器字段中指定。 直接段方法可用于对 FPM 空间没有大量要求的 PCI 功能,以减少访问 HMC 对象时产生的开销。 如果驱动程序能够分配足够大的物理连续页面范围来容纳支持特定 PCI 功能上加载的驱动程序所需的 FPM 所需的整个 PD 空间,则可以额外使用直接段方法。 如果驱动程序碰巧分配了物理连续的内存块,或者操作系统支持 2 MB 页面,此模式可以防止额外的地址查找并提高性能

9.3.2 对象缓存 RDMA 的主机内存缓存分为两个缓存。 一个缓存包含除四元哈希条目之外的所有 RDMA 对象。 四重哈希条目被放置在不同的缓存中,以便过滤机制可以引用它们。 当添加或删除四元哈希对象时,RDMA 固件负责更新四元哈希缓存中的四元哈希对象。 这两个缓存具有独立的寄存器,但共享相同的基本机制。 由于有两个组件引用四重哈希缓存,因此两个缓存的寄存器必须保持一致。 有关四散列主机内存缓存(包括寄存器和中断)的更多信息,请参阅第 9 节

E810 私有内存地址空间配置分为两步: 1. 将 HMC 相关资源划分为每个 PCI 功能资源。 2. 将生成的函数私有内存 (FPM) 划分为单独的对象。 为了简化资源分配,E810 提供了资源配置文件概念,该概念考虑了 PCI 功能的数量、启用协议引擎的 PCI 功能的数量以及操作系统环境来划分 HMC 专用内存空间和协议引擎 Doorbell 资源。 这些 HMC 资源配置文件用于对 HMC 段描述符表和协议引擎门铃资源进行分区。 软件在每个 PCI 功能的基础上执行 FPM 划分。 表 9-13 中描述了当前定义的 HMC 资源配置文件。 协议引擎门铃资源对 HMC 资源配置文件施加了限制,因为芯片上这些资源的数量是固定的。 有足够的门铃资源用于 256K 协议队列对和 512K 协议引擎完成队列。 这些资源必须在需要协议引擎功能的 PCI 功能之间进行划分。 协议引擎资源将在 11.1 节中进一步讨论

Linux内存管理数据结构的关系图

关键函数

  • rte_mem_virt2phy
  • rte_eal_memory_init
  • pte_to_pagemap_entry
  • smaps_pte_range
  • show_smap
  • pte_pfn: pte_pfn 函数用于从给定的 PTE(页表项条目)中提取物理页框号(PFN)。它接受一个 PTE 类型的参数 pte,并通过按位操作提取物理页框号
  • irdma_ctrl_init_hw 初始化硬件控制部分

详解

添加任务, proc文件系统以及操作函数:

代码语言:javascript
复制
/*
 * Tasks
 */
static const struct pid_entry tid_base_stuff[] = {
    DIR("fd",        S_IRUSR|S_IXUSR, proc_fd_inode_operations, proc_fd_operations),
    DIR("fdinfo",    S_IRUGO|S_IXUGO, proc_fdinfo_inode_operations, proc_fdinfo_operations),
    DIR("ns",    S_IRUSR|S_IXUGO, proc_ns_dir_inode_operations, proc_ns_dir_operations),
#ifdef CONFIG_NET
    DIR("net",        S_IRUGO|S_IXUGO, proc_net_inode_operations, proc_net_operations),
#endif
    REG("environ",   S_IRUSR, proc_environ_operations),
    REG("auxv",      S_IRUSR, proc_auxv_operations),
    ONE("status",    S_IRUGO, proc_pid_status),
    ONE("personality", S_IRUSR, proc_pid_personality),
    ONE("limits",    S_IRUGO, proc_pid_limits),
#ifdef CONFIG_SCHED_DEBUG
    REG("sched",     S_IRUGO|S_IWUSR, proc_pid_sched_operations),
#endif
    NOD("comm",      S_IFREG|S_IRUGO|S_IWUSR,
             &proc_tid_comm_inode_operations,
             &proc_pid_set_comm_operations, {}),
#ifdef CONFIG_HAVE_ARCH_TRACEHOOK
    ONE("syscall",   S_IRUSR, proc_pid_syscall),
#endif
    REG("cmdline",   S_IRUGO, proc_pid_cmdline_ops),
    ONE("stat",      S_IRUGO, proc_tid_stat),
    ONE("statm",     S_IRUGO, proc_pid_statm),
    REG("maps",      S_IRUGO, proc_pid_maps_operations),
#ifdef CONFIG_PROC_CHILDREN
    REG("children",  S_IRUGO, proc_tid_children_operations),
#endif
#ifdef CONFIG_NUMA
    REG("numa_maps", S_IRUGO, proc_pid_numa_maps_operations),
#endif
    REG("mem",       S_IRUSR|S_IWUSR, proc_mem_operations),
    LNK("cwd",       proc_cwd_link),
    LNK("root",      proc_root_link),
    LNK("exe",       proc_exe_link),
    REG("mounts",    S_IRUGO, proc_mounts_operations),
    REG("mountinfo",  S_IRUGO, proc_mountinfo_operations),
#ifdef CONFIG_PROC_PAGE_MONITOR
    REG("clear_refs", S_IWUSR, proc_clear_refs_operations),
    REG("smaps",     S_IRUGO, proc_pid_smaps_operations),
    REG("smaps_rollup", S_IRUGO, proc_pid_smaps_rollup_operations),
    REG("pagemap",    S_IRUSR, proc_pagemap_operations),  // 为/proc/xxx/pagemap 注册操作函数 proc_pagemap_operations
#endif
#ifdef CONFIG_SECURITY
    DIR("attr",      S_IRUGO|S_IXUGO, proc_attr_dir_inode_operations, proc_attr_dir_operations),
#endif
#ifdef CONFIG_KALLSYMS
    ONE("wchan",     S_IRUGO, proc_pid_wchan),
#endif
#ifdef CONFIG_STACKTRACE
    ONE("stack",      S_IRUSR, proc_pid_stack),
#endif
#ifdef CONFIG_SCHED_INFO
    ONE("schedstat", S_IRUGO, proc_pid_schedstat),
#endif
#ifdef CONFIG_LATENCYTOP
    REG("latency",  S_IRUGO, proc_lstats_operations),
#endif
#ifdef CONFIG_PROC_PID_CPUSET
    ONE("cpuset",    S_IRUGO, proc_cpuset_show),
#endif
#ifdef CONFIG_CGROUPS
    ONE("cgroup",  S_IRUGO, proc_cgroup_show),
#endif
#ifdef CONFIG_PROC_CPU_RESCTRL
    ONE("cpu_resctrl_groups", S_IRUGO, proc_resctrl_show),
#endif
    ONE("oom_score", S_IRUGO, proc_oom_score),
    REG("oom_adj",   S_IRUGO|S_IWUSR, proc_oom_adj_operations),
    REG("oom_score_adj", S_IRUGO|S_IWUSR, proc_oom_score_adj_operations),
#ifdef CONFIG_AUDIT
    REG("loginuid",  S_IWUSR|S_IRUGO, proc_loginuid_operations),
    REG("sessionid",  S_IRUGO, proc_sessionid_operations),
#endif
#ifdef CONFIG_FAULT_INJECTION
    REG("make-it-fail", S_IRUGO|S_IWUSR, proc_fault_inject_operations),
    REG("fail-nth", 0644, proc_fail_nth_operations),
#endif
#ifdef CONFIG_TASK_IO_ACCOUNTING
    ONE("io",   S_IRUSR, proc_tid_io_accounting),
#endif
#ifdef CONFIG_USER_NS
    REG("uid_map",    S_IRUGO|S_IWUSR, proc_uid_map_operations),
    REG("gid_map",    S_IRUGO|S_IWUSR, proc_gid_map_operations),
    REG("projid_map", S_IRUGO|S_IWUSR, proc_projid_map_operations),
    REG("setgroups",  S_IRUGO|S_IWUSR, proc_setgroups_operations),
#endif
#ifdef CONFIG_LIVEPATCH
    ONE("patch_state",  S_IRUSR, proc_pid_patch_state),
#endif
#ifdef CONFIG_PROC_PID_ARCH_STATUS
    ONE("arch_status", S_IRUGO, proc_pid_arch_status),
#endif
#ifdef CONFIG_SECCOMP_CACHE_DEBUG
    ONE("seccomp_cache", S_IRUSR, proc_pid_seccomp_cache),
#endif
#ifdef CONFIG_KSM
    ONE("ksm_merging_pages",  S_IRUSR, proc_pid_ksm_merging_pages),
    ONE("ksm_stat",  S_IRUSR, proc_pid_ksm_stat),
#endif
};

proc文件系统(pagemap页映射)函数操作表:

代码语言:javascript
复制
const struct file_operations proc_pagemap_operations = {
    .llseek     = mem_lseek, /* borrow this */
    .read       = pagemap_read,
    .open       = pagemap_open,
    .release    = pagemap_release,
    .unlocked_ioctl = do_pagemap_cmd,
    .compat_ioctl   = do_pagemap_cmd,
};

读取/proc/xxx/pagemap则执行钩子函数pagemap_read(读取页映射)

代码语言:javascript
复制
pagemap_read -> Maps4:添加 /proc/pid/pagemap 接口,该接口为地址空间中的每个页面提供到其物理页帧号的映射,允许精确确定哪些页面被映射以及哪些页面在进程之间共享。 此版本中的新增内容: - 标头再次消失(根据 Dave Hansen 和 Alan Cox 的建议) - 64 位条目(根据与 Andi Kleen 的讨论) - 交换导出的 pte 信息(来自 Dave Hansen) - 页面遍历器回调以查找漏洞(来自 Dave Hansen) - 直接 put_user I/O(根据 Rusty Russell 的建议)此补丁折叠在清理中并交换 Dave Hansen 的 PTE 支持
    file_ns_capable
    mmap_read_lock_killable
    untagged_addr_remote
    mmap_read_lock_killable
    walk_page_range(mm, start_vaddr, end, &pagemap_ops, &pm) -> walk_page_range - 使用回调遍历内存映射的页表:起始地址:结束地址:为树的每个级别调用的回调集递归地遍历 VMA 中内存区域的页表,调用提供的回调。 回调按顺序调用(第一个 PGD、第一个 PUD、第一个 PMD、第一个 PTE、第二个 PTE...第二个 PMD 等)。 如果省略较低级别的回调,则行走深度会减少。 每个回调接收一个入口指针以及关联范围的开始和结束,以及用于访问 ->private 或 ->mm 字段的原始 mm_walk 的副本。 通常不加锁,但分割透明大页可能会加页表锁。 如果需要,底层迭代器将从 highmem 映射 PTE 目录。 如果任何回调返回非零值,则遍历将中止并将返回值传播回调用者。 否则返回 0。 如果 walk->hugetlb_entry 为 !NULL,则 walk->mm->mmap_sem 必须至少保持读取状态
    copy_to_user

遍历执行页操作函数: pagemap_pmd_range

代码语言:javascript
复制
static const struct mm_walk_ops pagemap_ops = {
    .pmd_entry  = pagemap_pmd_range,
    .pte_hole   = pagemap_pte_hole,
    .hugetlb_entry  = pagemap_hugetlb_range,
    .walk_lock  = PGWALK_RDLOCK,
};
代码语言:javascript
复制
pagemap_pmd_range -> pagemap:将 mm 传递给 pagewalkers ,我们现在至少需要这个来检测大页,因为 powerpc 需要 vm_area_struct 来确定虚拟地址是否引用大页(它的 pmd_huge() 不起作用)。 对于其他一些用户来说它也可能派上用场
    pmd_trans_huge_lock
    ...
    for pte
        内核态实现pagemap proc接口的代码位于: fs/proc/task_mmu.c, 把PTE转换为pagemap_entry
        pte_to_pagemap_entry -> proc:报告 /proc/pid/pagemap 中的文件/匿名位,这是安德鲁提议的实现,扩展页面映射文件位以报告任务工作集缺少的内容。 工作集检测的问题是多方面的。 在 criu(检查点/恢复)项目中,我们将任务的内存转储到图像文件中,为了正确执行此操作,我们需要检测映射中的哪些页面真正在使用。 我虽然可以帮助解决这个问题,但 mincore 系统调用却没有。 首先,它不报告交换的页面,因此我们无法找出要转储的匿名映射的哪些部分。 接下来,它会报告页面缓存中存在的页面,即使它们没有被映射,但这并不意味着它们没有受到威胁。 请注意,交换页的问题至关重要——我们必须将交换页转储到映像文件。 但是文件页面的问题是优化 - 我们可以将所有文件页面进行映像,这是正确的,但是如果我们知道页面未映射或未受到限制,我们可以将它们从转储文件中删除。 转储仍然是自洽的,尽管大小明显较小(在实际应用程序中最多小 10 倍)。 Andrew 注意到,proc pagemap 文件解决了上述 3 个问题中的 2 个——它报告页面是否存在或交换,并且不报告未映射的页面缓存页面。 但是,它无法区分受限制的文件页面和不受限制的文件页面。 我想在此文件中最后一个未使用的位来报告映射到相应 pte 的页面是否为 PageAnon
            pte_present(pte)
            frame = pte_pfn(pte)
            make_pme(frame, flags)
        add_to_pagemap
    ...

大页

两种大页类型

如果您需要一个命名的大页面映射,您可以映射一个引用hugetlbfs 文件系统上的文件的文件描述符。 大页是从保留池中分配的。 您可以使用内核命令行参数hugepages 或在运行时使用procfs 或sysfs 接口来保留大页。 请阅读有关大页的 Linux 内核文档,以获取有关如何保留大页的更多信息。 保留默认大小的大页的最简单方法是使用 procfs 接口: echo 20 > /proc/sys/vm/nr_hugepages

Hugetlbfs

  本文的例子摘自 Linux 内核源码中提供的有关说明文档 (Documentation/vm/hugetlbpage.txt) 。使用 hugetlbfs 之前,首先需要在编译内核 (make menuconfig) 时配置CONFIG_HUGETLB_PAGECONFIG_HUGETLBFS选项,这两个选项均可在 File systems 内核配置菜单中找到。

代码语言:javascript
复制
  mount none /mnt/huge -t hugetlbfs

  此后,只要是在 /mnt/huge/ 目录下创建的文件,将其映射到内存中时都会使用 2MB 作为分页的基本单位。值得一提的是,hugetlbfs 中的文件是不支持读 / 写系统调用 ( 如read()write()等 ) 的,一般对它的访问都是以内存映射的形式进行的。为了更好地介绍大页面的应用,接下来将给出一个大页面应用的例子,该例子同样也是摘自于上述提到的内核文档,只是略有简化。

代码语言:javascript
复制
清单 1. Linux 大页面应用示例
 #include <fcntl.h>
 #include <sys/mman.h>
 #include <errno.h>
​
 #define MAP_LENGTH      (10*1024*1024)
​
 int main()
 {
    int fd;
    void * addr;
​
    /* create a file in hugetlb fs */
    fd = open("/mnt/huge/test", O_CREAT | O_RDWR);
    if(fd < 0){
        perror("Err: ");
        return -1;
    }
​
    /* map the file into address space of current application process */
    addr = mmap(0, MAP_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if(addr == MAP_FAILED){
        perror("Err: ");
        close(fd);
        unlink("/mnt/huge/test");
        return -1;
    }
​
    /* from now on, you can store application data on huage pages via addr */
​
    munmap(addr, MAP_LENGTH);
    close(fd);
    unlink("/mnt/huge/test");
    return 0;
 }

对于系统中大页面的统计信息可以在 Proc 特殊文件系统(/proc)中查到,如/proc/sys/vm/nr_hugepages给出了当前内核中配置的大页面的数目,也可以通过该文件配置大页面的数目,如:

代码语言:javascript
复制
  echo 20 > /proc/sys/vm/nr_hugepages
代码语言:javascript
复制
另外,hugepage的默认大小也可以通过配置内核启动参数“default_hugepagesz”指定,例如:default_hugepagesz=4M,指定default_hstate_size的大小为4M,其内核实现如下:

mmap

Linux中的Mmap(Memory Map)是一种内存映射机制,它允许将文件或设备的一部分映射到进程的虚拟内存空间。通过使用Mmap,进程可以直接访问被映射对象的内容,而无需进行传统的读取和写入操作。

在内存映射过程中,操作系统会将文件数据按页(通常是4KB)进行划分,并在物理内存和虚拟地址空间之间建立对应关系。当进程需要访问文件时,它只需要使用指针来读写相应的内存地址即可,而无需手动调用 read() 或 write() 函数进行 I/O 操作。这种直接访问的方式可以提高读写效率,并且简化了程序逻辑

mmap与shm对比

mmap的机制如:就是在磁盘上建立一个文件,每个进程存储器里面,单独开辟一个空间来进行映射。如果多进程的话,那么不会对实际的物理存储器(主存)消耗太大。 shm的机制:每个进程的共享内存都直接映射到实际物理存储器里面。

内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已 经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存。由此可以看出,使用内 存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由 系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的 作用。另外,实际工程中的系统往往需要在多个进程之间共享数据,如果数据量小,处理方法是灵活多变的,如果共享数据容量巨大,那么就需要借助于内存映射文 件来进行。实际上,内存映射文件正是解决本地多个进程间数据共享的最有效方法

  • 1、mmap有两种方式,一种是映射内存,它把普通文件映射为实际物理内存页,访问它就和访问物理内存一样(这也就和shm的功能一样了)(同时不用刷新到文件)
  • 2、mmap可以映射文件,不确定会不会像windows“内存映射文件”一样的功能,如果是,那么他就能映射好几G甚至好几百G的内存数据,对大数据处理将提供强大功能了???
  • 3、shm只做内存映射,和mmap第一个功能一样!只不过不是普通文件而已,但都是物理内存。
代码语言:javascript
复制
 //建立共享内存区域
  intshared_id;
  char *region;
  const intshm_size = 1024;
  
  shared_id = shmget(IPC_PRIVATE,//保证使用唯一ID
            shm_size,
            IPC_CREAT | IPC_EXCL |//创建一个新的内存区域
            S_IRUSR | S_IWUSR);//使当前用户可以读写这个区域
  
  //交叉进程或生成进程.
  
  //将新建的内存区域放入进程/线程
  region = (char*) shmat(segment_id, 0, 0);
  
  //其他程序代码
  ...
  
  //将各个进程/线程分离出来
  shmdt(region);
  
  //破坏掉共享内存区域
  shmctl(shared_id, IPC_RMID, 0);

使用内存映射的缺点是速度不如共享内存快。如果凑巧文件很大,所需要的虚拟内存就会很大,这样会造成整体性能下降

请记住,mmap() 仅保留内存页(除非使用 MAP_POPULATE 标志)。 分配是惰性完成的。 它会在第一次访问底层内存页面时发生,而不是更早。 如果单个 mmap() 调用已保留多个页面,则访问单个页面不会导致分配其他页面。 我强烈反对将 MAP_NORESERVE 标志与 MAP_HUGETLB 结合使用。 当没有足够的大页面可用时,使用 MAP_NORESERVE 的 mmap() 会产生令人讨厌的行为。 mmap() 调用总是成功,但稍后,在第一次访问无法分配的内存页时,会出现 SIGBUS 信号。 这使得错误处理变得非常复杂,并且使得 Paul Khuong 在 Twitter 上提出的解决方案几乎毫无用处。 不用担心交换——在 Linux 中,大页面无论如何都不能被换出。 请求特定大小的大页面您可以在调用 mmap() 时通过传递一个标志来手动指定所需的页面大小:

mmap内存映射原理

mmap内存映射的实现过程,总的来说可以分为三个阶段:

(一)进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域

  • 1、进程在用户空间调用库函数mmap,原型:void *mmap(void *start, size_t length, int prot,int flags, int fd, off_t offset);
  • 2、在当前进程的虚拟地址空间中,寻找一段空闲的满足要求的连续虚拟地址
  • 3、为此虚拟区分配一个vm_area_struct结构,接着对这个结构的各个域进行了初始化
  • 4、将新建的虚拟区结构(vm_area_struct)插入进程的虚拟地址区域链表或树中

(二)调用内核空间的系统调用函数mmap(不同于用户空间函数),实现文件物理地址和进程虚拟地址的一一映射关系

  • 5、为映射分配了新的虚拟地址区域后,通过待映射的文件指针,在文件描述符表中找到对应的文件描述符,通过文件描述符,链接到内核“已打开文件集”中该文件的文件结构体(struct file),每个文件结构体维护着和这个已打开文件相关各项信息。
  • 6、通过该文件的文件结构体,链接到file_operations模块,调用内核函数mmap,其原型为:int mmap(struct file *filp, struct vm_area_struct *vma),不同于用户空间库函数。
  • 7、内核mmap函数通过虚拟文件系统inode模块定位到文件磁盘物理地址。
  • 8、通过remap_pfn_range函数建立页表,即实现了文件地址和虚拟地址区域的映射关系。此时,这片虚拟地址并没有任何数据关联到主存中。

(三)进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理内存(主存)的拷贝

注:前两个阶段仅在于创建虚拟区间并完成地址映射,但是并没有将任何文件数据的拷贝至主存。真正的文件读取是当进程发起读或写操作时。

  • 9、进程的读或写操作访问虚拟地址空间这一段映射地址,通过查询页表,发现这一段地址并不在物理页面上。因为目前只建立了地址映射,真正的硬盘数据还没有拷贝到内存中,因此引发缺页异常。
  • 10、缺页异常进行一系列判断,确定无非法操作后,内核发起请求调页过程。
  • 11、调页过程先在交换缓存空间(swap cache)中寻找需要访问的内存页,如果没有则调用nopage函数把所缺的页从磁盘装入到主存中。
  • 12、之后进程即可对这片主存进行读或者写的操作,如果写操作改变了其内容,一定时间后系统会自动回写脏页面到对应磁盘地址,也即完成了写入到文件的过程。

注:修改过的脏页面并不会立即更新回文件中,而是有一段时间的延迟,可以调用msync()来强制同步, 这样所写的内容就能立即保存到文件里了。

文件读写的基本流程

4.1读文件

1、进程调用库函数向内核发起读文件请求;

2、内核通过检查进程的文件描述符定位到虚拟文件系统的已打开文件列表表项;

3、调用该文件可用的系统调用函数read()

3、read()函数通过文件表项链接到目录项模块,根据传入的文件路径,在目录项模块中检索,找到该文件的inode;

4、在inode中,通过文件内容偏移量计算出要读取的页;

5、通过inode找到文件对应的address_space;

6、在address_space中访问该文件的页缓存树,查找对应的页缓存结点:

  • (1)如果页缓存命中,那么直接返回文件内容;
  • (2)如果页缓存缺失,那么产生一个页缺失异常,创建一个页缓存页,同时通过inode找到文件该页的磁盘地址,读取相应的页填充该缓存页;重新进行第6步查找页缓存;

7、文件内容读取成功。

4.2写文件

前5步和读文件一致,在address_space中查询对应页的页缓存是否存在:

6、如果页缓存命中,直接把文件内容修改更新在页缓存的页中。写文件就结束了。这时候文件修改位于页缓存,并没有写回到磁盘文件中去。

7、如果页缓存缺失,那么产生一个页缺失异常,创建一个页缓存页,同时通过inode找到文件该页的磁盘地址,读取相应的页填充该缓存页。此时缓存页命中,进行第6步。

8、一个页缓存中的页如果被修改,那么会被标记成脏页。脏页需要写回到磁盘中的文件块。有两种方式可以把脏页写回磁盘:

  • (1)手动调用sync()或者fsync()系统调用把脏页写回
  • (2)pdflush进程会定时把脏页写回到磁盘

同时注意,脏页不能被置换出内存,如果脏页正在被写回,那么会被设置写回标记,这时候该页就被上锁,其他写请求被阻塞直到锁释放。

内存说明

代码语言:javascript
复制
* /proc/pid/pagemap.  This file lets a userspace process find out which
   physical frame each virtual page is mapped to.  It contains one 64-bit
   value for each virtual page, containing the following data (from
   fs/proc/task_mmu.c, above pagemap_read):

    * Bits 0-54  page frame number (PFN) if present//present为1时,bit0-54表示物理页号
    * Bits 0-4   swap type if swapped
    * Bits 5-54  swap offset if swapped
    * Bit  55    pte is soft-dirty (see Documentation/vm/soft-dirty.txt)
    * Bit  56    page exclusively mapped (since 4.2)
    * Bits 57-60 zero
    * Bit  61    page is file-page or shared-anon (since 3.5)
    * Bit  62    page swapped
    * Bit  63    page present//如果为1,表示当前物理页在内存中;为0,表示当前物理页不在内存中

DPDK使用大页

代码语言:javascript
复制
mount -t hugetlbfs hugetlbfs /dev/hugepages (挂载默认的hugeage大小)
mount -t hugetlbfs none /dev/hugepages_2mb -o pagesize=2MB(挂载2M的)
1G大页和2M大页必须挂载了才能使用。挂载其中一个,DPDK也能正常运行。
代码语言:javascript
复制
// lib/eal/linux/eal_memory.c
map_all_hugepages()
	// 拼接文件名 /dev/hugepages/rte_hugepage_%s
	eal_get_hugefile_path(hf->filepath, sizeof(hf->filepath),
				hpi->hugedir, hf->file_id);
	// 在/dev/hugepages/ 目录下 新建文件
	fd = open(hf->filepath, O_CREAT | O_RDWR, 0600);
	// 获取物理巨页。注意:多次mmap(),该文件仍 只有1g大小
	virtaddr = mmap(NULL, hugepage_sz, PROT_READ | PROT_WRITE,
			MAP_SHARED | MAP_POPULATE, fd, 0);

DPDK文件与数据映射关系

dpdk 多进程模式下,进程间通过 文件来共享文件,通过文件锁来同步

代码语言:javascript
复制
/var/run/dpdk/rte/hugepage_data  =>  hugepage_file []
/dev/hugepage/rte_hugepage_0     => huge page
/var/run/dpdk/rte/fbarray_0      => rte_fbarray->data
/var/run/dpdk/rte/fbarray_memzone
/var/run/dpdk/rte/config         => rte_mem_config

DPDK检查CPU特性/标记位

代码语言:javascript
复制
const struct feature_entry rte_cpu_feature_table[] = {
	FEAT_DEF(SSE3, 0x00000001, 0, RTE_REG_ECX,  0)
	FEAT_DEF(PCLMULQDQ, 0x00000001, 0, RTE_REG_ECX,  1)
	FEAT_DEF(DTES64, 0x00000001, 0, RTE_REG_ECX,  2)
	FEAT_DEF(MONITOR, 0x00000001, 0, RTE_REG_ECX,  3)
	FEAT_DEF(DS_CPL, 0x00000001, 0, RTE_REG_ECX,  4)
	FEAT_DEF(VMX, 0x00000001, 0, RTE_REG_ECX,  5)
	FEAT_DEF(SMX, 0x00000001, 0, RTE_REG_ECX,  6)
	FEAT_DEF(EIST, 0x00000001, 0, RTE_REG_ECX,  7)
	FEAT_DEF(TM2, 0x00000001, 0, RTE_REG_ECX,  8)
	FEAT_DEF(SSSE3, 0x00000001, 0, RTE_REG_ECX,  9)
	FEAT_DEF(CNXT_ID, 0x00000001, 0, RTE_REG_ECX, 10)
	FEAT_DEF(FMA, 0x00000001, 0, RTE_REG_ECX, 12)
	FEAT_DEF(CMPXCHG16B, 0x00000001, 0, RTE_REG_ECX, 13)
	FEAT_DEF(XTPR, 0x00000001, 0, RTE_REG_ECX, 14)
	FEAT_DEF(PDCM, 0x00000001, 0, RTE_REG_ECX, 15)
	FEAT_DEF(PCID, 0x00000001, 0, RTE_REG_ECX, 17)
	FEAT_DEF(DCA, 0x00000001, 0, RTE_REG_ECX, 18)
	FEAT_DEF(SSE4_1, 0x00000001, 0, RTE_REG_ECX, 19)
	FEAT_DEF(SSE4_2, 0x00000001, 0, RTE_REG_ECX, 20)
	FEAT_DEF(X2APIC, 0x00000001, 0, RTE_REG_ECX, 21)
	FEAT_DEF(MOVBE, 0x00000001, 0, RTE_REG_ECX, 22)
	FEAT_DEF(POPCNT, 0x00000001, 0, RTE_REG_ECX, 23)
	FEAT_DEF(TSC_DEADLINE, 0x00000001, 0, RTE_REG_ECX, 24)
	FEAT_DEF(AES, 0x00000001, 0, RTE_REG_ECX, 25)
	FEAT_DEF(XSAVE, 0x00000001, 0, RTE_REG_ECX, 26)
	FEAT_DEF(OSXSAVE, 0x00000001, 0, RTE_REG_ECX, 27)
	FEAT_DEF(AVX, 0x00000001, 0, RTE_REG_ECX, 28)
	FEAT_DEF(F16C, 0x00000001, 0, RTE_REG_ECX, 29)
	FEAT_DEF(RDRAND, 0x00000001, 0, RTE_REG_ECX, 30)
	FEAT_DEF(HYPERVISOR, 0x00000001, 0, RTE_REG_ECX, 31)

	FEAT_DEF(FPU, 0x00000001, 0, RTE_REG_EDX,  0)
	FEAT_DEF(VME, 0x00000001, 0, RTE_REG_EDX,  1)
	FEAT_DEF(DE, 0x00000001, 0, RTE_REG_EDX,  2)
	FEAT_DEF(PSE, 0x00000001, 0, RTE_REG_EDX,  3)
	FEAT_DEF(TSC, 0x00000001, 0, RTE_REG_EDX,  4)
	FEAT_DEF(MSR, 0x00000001, 0, RTE_REG_EDX,  5)
	FEAT_DEF(PAE, 0x00000001, 0, RTE_REG_EDX,  6)
	FEAT_DEF(MCE, 0x00000001, 0, RTE_REG_EDX,  7)
	FEAT_DEF(CX8, 0x00000001, 0, RTE_REG_EDX,  8)
	FEAT_DEF(APIC, 0x00000001, 0, RTE_REG_EDX,  9)
	FEAT_DEF(SEP, 0x00000001, 0, RTE_REG_EDX, 11)
	FEAT_DEF(MTRR, 0x00000001, 0, RTE_REG_EDX, 12)
	FEAT_DEF(PGE, 0x00000001, 0, RTE_REG_EDX, 13)
	FEAT_DEF(MCA, 0x00000001, 0, RTE_REG_EDX, 14)
	FEAT_DEF(CMOV, 0x00000001, 0, RTE_REG_EDX, 15)
	FEAT_DEF(PAT, 0x00000001, 0, RTE_REG_EDX, 16)
	FEAT_DEF(PSE36, 0x00000001, 0, RTE_REG_EDX, 17)
	FEAT_DEF(PSN, 0x00000001, 0, RTE_REG_EDX, 18)
	FEAT_DEF(CLFSH, 0x00000001, 0, RTE_REG_EDX, 19)
	FEAT_DEF(DS, 0x00000001, 0, RTE_REG_EDX, 21)
	FEAT_DEF(ACPI, 0x00000001, 0, RTE_REG_EDX, 22)
	FEAT_DEF(MMX, 0x00000001, 0, RTE_REG_EDX, 23)
	FEAT_DEF(FXSR, 0x00000001, 0, RTE_REG_EDX, 24)
	FEAT_DEF(SSE, 0x00000001, 0, RTE_REG_EDX, 25)
	FEAT_DEF(SSE2, 0x00000001, 0, RTE_REG_EDX, 26)
	FEAT_DEF(SS, 0x00000001, 0, RTE_REG_EDX, 27)
	FEAT_DEF(HTT, 0x00000001, 0, RTE_REG_EDX, 28)
	FEAT_DEF(TM, 0x00000001, 0, RTE_REG_EDX, 29)
	FEAT_DEF(PBE, 0x00000001, 0, RTE_REG_EDX, 31)

	FEAT_DEF(DIGTEMP, 0x00000006, 0, RTE_REG_EAX,  0)
	FEAT_DEF(TRBOBST, 0x00000006, 0, RTE_REG_EAX,  1)
	FEAT_DEF(ARAT, 0x00000006, 0, RTE_REG_EAX,  2)
	FEAT_DEF(PLN, 0x00000006, 0, RTE_REG_EAX,  4)
	FEAT_DEF(ECMD, 0x00000006, 0, RTE_REG_EAX,  5)
	FEAT_DEF(PTM, 0x00000006, 0, RTE_REG_EAX,  6)

	FEAT_DEF(MPERF_APERF_MSR, 0x00000006, 0, RTE_REG_ECX,  0)
	FEAT_DEF(ACNT2, 0x00000006, 0, RTE_REG_ECX,  1)
	FEAT_DEF(ENERGY_EFF, 0x00000006, 0, RTE_REG_ECX,  3)

	FEAT_DEF(FSGSBASE, 0x00000007, 0, RTE_REG_EBX,  0)
	FEAT_DEF(BMI1, 0x00000007, 0, RTE_REG_EBX,  3)
	FEAT_DEF(HLE, 0x00000007, 0, RTE_REG_EBX,  4)
	FEAT_DEF(AVX2, 0x00000007, 0, RTE_REG_EBX,  5)
	FEAT_DEF(SMEP, 0x00000007, 0, RTE_REG_EBX,  7)
	FEAT_DEF(BMI2, 0x00000007, 0, RTE_REG_EBX,  8)
	FEAT_DEF(ERMS, 0x00000007, 0, RTE_REG_EBX,  9)
	FEAT_DEF(INVPCID, 0x00000007, 0, RTE_REG_EBX, 10)
	FEAT_DEF(RTM, 0x00000007, 0, RTE_REG_EBX, 11)
	FEAT_DEF(AVX512F, 0x00000007, 0, RTE_REG_EBX, 16)
	FEAT_DEF(AVX512DQ, 0x00000007, 0, RTE_REG_EBX, 17)
	FEAT_DEF(RDSEED, 0x00000007, 0, RTE_REG_EBX, 18)
	FEAT_DEF(AVX512IFMA, 0x00000007, 0, RTE_REG_EBX, 21)
	FEAT_DEF(AVX512CD, 0x00000007, 0, RTE_REG_EBX, 28)
	FEAT_DEF(AVX512BW, 0x00000007, 0, RTE_REG_EBX, 30)
	FEAT_DEF(AVX512VL, 0x00000007, 0, RTE_REG_EBX, 31)

	FEAT_DEF(AVX512VBMI, 0x00000007, 0, RTE_REG_ECX,  1)
	FEAT_DEF(WAITPKG, 0x00000007, 0, RTE_REG_ECX,  5)
	FEAT_DEF(AVX512VBMI2, 0x00000007, 0, RTE_REG_ECX,  6)
	FEAT_DEF(GFNI, 0x00000007, 0, RTE_REG_ECX,  8)
	FEAT_DEF(VAES, 0x00000007, 0, RTE_REG_ECX,  9)
	FEAT_DEF(VPCLMULQDQ, 0x00000007, 0, RTE_REG_ECX, 10)
	FEAT_DEF(AVX512VNNI, 0x00000007, 0, RTE_REG_ECX, 11)
	FEAT_DEF(AVX512BITALG, 0x00000007, 0, RTE_REG_ECX, 12)
	FEAT_DEF(AVX512VPOPCNTDQ, 0x00000007, 0, RTE_REG_ECX, 14)
	FEAT_DEF(CLDEMOTE, 0x00000007, 0, RTE_REG_ECX, 25)
	FEAT_DEF(MOVDIRI, 0x00000007, 0, RTE_REG_ECX, 27)
	FEAT_DEF(MOVDIR64B, 0x00000007, 0, RTE_REG_ECX, 28)

	FEAT_DEF(AVX512VP2INTERSECT, 0x00000007, 0, RTE_REG_EDX,  8)

	FEAT_DEF(LAHF_SAHF, 0x80000001, 0, RTE_REG_ECX,  0)
	FEAT_DEF(LZCNT, 0x80000001, 0, RTE_REG_ECX,  4)

	FEAT_DEF(SYSCALL, 0x80000001, 0, RTE_REG_EDX, 11)
	FEAT_DEF(XD, 0x80000001, 0, RTE_REG_EDX, 20)
	FEAT_DEF(1GB_PG, 0x80000001, 0, RTE_REG_EDX, 26)
	FEAT_DEF(RDTSCP, 0x80000001, 0, RTE_REG_EDX, 27)
	FEAT_DEF(EM64T, 0x80000001, 0, RTE_REG_EDX, 29)

	FEAT_DEF(INVTSC, 0x80000007, 0, RTE_REG_EDX,  8)
};

参考

linux内核添加pagemap接口(/proc/pid/pagemap): https://github.com/ssbandjl/linux/commit/85863e475e59afb027b0113290e3796ee6020b7d

DPDK源码分析: Memory: https://zzqcn.github.io/opensource/dpdk/code-analysis/mem.html

用户态进程如何得到虚拟地址对应的物理地址/宋宝华-linux阅马场: https://cloud.tencent.com/developer/article/1722573, https://mp.weixin.qq.com/s/OJJE-Up-U0C9uGHRCEuz6w

linux pagemap 是内核中的一组新接口(自 2.6.25 起),允许用户空间程序通过读取“/proc”中的文件来检查页表和相关信息

检查进程页表/内存页映射: https://www.kernel.org/doc/Documentation/admin-guide/mm/pagemap.rst, https://www.kernel.org/doc/html/v4.18/admin-guide/mm/pagemap.html

Linux下如何在进程中获取虚拟地址对应的物理地址: https://kongkongk.github.io/2020/06/30/address-translation/

DPDK mempool和mbuf的理解: https://zhuanlan.zhihu.com/p/578515617

ESearch:Linux进程内存用量分析之内存映射篇(58): https://www.jianshu.com/p/bff46f531920

Linux proc文件系统: https://www.kernel.org/doc/html/latest/filesystems/proc.html?highlight=Pss, https://man7.org/linux/man-pages/man5/proc.5.html

Linux /proc/$pid/smaps的含义: https://blog.csdn.net/petib_wangwei/article/details/38225929

Flush-Cache/Page-Lock/Flush-TLB说明: https://blog.csdn.net/qianlong4526888/article/details/81627959

Linux内存管理 (19)总结内存管理数据结构和API: https://www.cnblogs.com/arnoldlu/p/8335568.html

RDMA IB uverbs 内存固定实现: https://github.com/ssbandjl/linux/commit/eb8ffbfed50e7945c024a80e3688d5beffa3b641

RDMA IB/uverbs: 暴露 ib_umem_get()/ib_umem_release() 到模块: https://github.com/ssbandjl/linux/commit/f7c6a7b5d59980b076abbf2ceeb8735591290285

内核抢占和低延迟/cond_resched: https://blog.csdn.net/su_linux/article/details/15500053

pin内存: https://www.kernel.org/doc/html/v6.6/core-api/pin_user_pages.html#case-2-rdma, https://lwn.net/Articles/807108/

ibv_reg_mr源码分析: https://gwzlchn.github.io/202208/rdma-stack-02/#

intel PBLE 资源管理器实现: https://github.com/ssbandjl/linux/commit/e8c4dbc2fcacf5a7468d312168bb120c27c38b32

海思RDMA Linux hns驱动分析: https://blog.csdn.net/lincolnjunior_lj/article/details/131358376

intel 实现设备初始化例程、中断设置和分配对象位图跟踪结构。 另外,添加设备特定属性和寄存器定义: https://github.com/ssbandjl/linux/commit/44d9e52977a1b90b0db1c7f8b197c218e9226520

浅谈Linux Hugetlbfs内核源码简析-Hugetlbfs初始化: https://zhuanlan.zhihu.com/p/549072447, https://www.cnblogs.com/MerlinJ/p/4053689.html

linux mmap/shm等原理/文件读写: https://zhuanlan.zhihu.com/p/685848279

mmap参数: https://man7.org/linux/man-pages/man2/mmap.2.html

dpdk内存管理/原理/大页管理及申请/使用: https://blog.csdn.net/qq_20679687/article/details/126570185, https://blog.csdn.net/shaoyunzhe/article/details/89948428

hugetlbfs/大页内核实现/原理: http://blog.chinaunix.net/uid-28541347-id-5783934.html

晓兵(ssbandjl)

博客: https://cloud.tencent.com/developer/user/5060293/articles | https://logread.cn | https://blog.csdn.net/ssbandjl | https://www.zhihu.com/people/ssbandjl/posts

DPU专栏

https://cloud.tencent.com/developer/column/101987

技术会友: 欢迎对DPU/智能网卡/卸载/网络,存储加速/安全隔离等技术感兴趣的朋友加入DPU技术交流群

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • DPDK巨页地址管理/Linux内核内存管理/内存映射/pagemap/rdma内存/注册
  • 术语
  • 简介
  • Intel E810的PBLE/HMC
  • Linux内存管理数据结构的关系图
  • 关键函数
  • 详解
  • 大页
    • 两种大页类型
    • Hugetlbfs
    • mmap
      • mmap与shm对比
        • 4.1读文件
        • 4.2写文件
    • mmap内存映射原理
    • 文件读写的基本流程
    • 内存说明
    • DPDK使用大页
    • DPDK文件与数据映射关系
    • DPDK检查CPU特性/标记位
    • 参考
    • 晓兵(ssbandjl)
      • DPU专栏
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档