(一)一些概念:
分页:将物理内存分成固定大小的块,按照页来进行分配和释放;一般带下为4K(2^12)个字节;
大页:比如大小为2M(2^20)和2G(2^32)字节大小的页;
虚拟地址:软件编码通过虚拟地址来访问内存;由处理器将虚拟地址转换成物理地址;(虚拟地址对应虚拟内存,虚拟内存对应了numa系统的node节点)
页表:形成页目录表、页表、内容页的层级结构(为什么要三层结构?)用于虚拟地址到物理地址的转换;以虚拟地址2^32举例:
(二)TLB
在分层寻址的基础上,又引入了TLB的概念;TLB可以理解为一个缓存在cache中维护着前20位[31:12]对应关系的表项;如果能在TLB中匹配到逻辑地址,就能迅速通过页表项和[11:0]得到物理地址;反之,无法匹配则为不命中;
既然这样,如果TLB足够大,所有表项都缓存在cache中,保证每次命中,则转换过程可以非常快;而实际上TLB表项很小(受限于cache本身的大小?);
(三)大页
这时候大页的优势就体现出来了;通过配置大页,需要的TLB表项就很小;如果是以2M作为分页,只需要一个表项就可保证全部命中了~
(四)如何配置大页
操作环境的NUMA拓扑结构:
可以看到,一共有2个socket(CPU插槽-物理概念),每个socket有2个node,每个node有24个core,每个core单线程,共有96个threads。
大页分配可以通过linux启动参数设置或者启动后动态预留。
假设需要在node0上分配1024个2M大页,在linux中可以通过动态预留:
echo 1024 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages
dpdk中在node0节点出输入1024即可:
./home/z00383571/Dpdk_18.05/dpdk-stable-18.05.1/usertools/dpdk-setup.sh
而对于1G大小的页,必须通过设置启动参数的方式预留,不支持动态修改:
EulerOS:~ # cat /proc/cmdline BOOT_IMAGE=hugepagesz=1G default_hugepagesz=1G hugepages=4
(四)dpdk的大页使用
eal_hugepage_info_init() : //获取大页配置 eal_hugepage_info_read(); //根据配置初始化并映射内存 更具体的代码分析后续单独记录
使用大页需要mount到某个路径,比如:
#mkdir /mnt/huge
#mount -t hugetlbfs nodev /mnt/huge
可以修改/etc/fstab来避免每次开机重新设置
nodev /mnt/huge hugetlbfs defaults 0 0
对于1GB大小的大页。需要使用如下:
nodev /mnt/huge_1GB hugetlbfs pagesize=1GB 0 0
提问1:为什么需要分成三层的结构去进行查找?(如果改成两层,可以减少一次内存访问)
答:依旧以2^32虚拟地址为例,为了管理4K内存页,需要2^20个表项,因此需要建立一个4M的页表,即1024个物理页,内存代价过大。
提问2:假设pci网卡设备挂在cpu1的pcie总线上,大页申请在cpu0的node0上,会不会导致转发性能低?
答:跨socket使用内存,必然会导致性能降低;