原文:https://blog.csdn.net/jy1075518049/article/details/43610569
两种内存管理方式:
1、内存寻址--段式管理
三个名词:逻辑地址、线性地址、物理地址
物理地址:最容易理解的,它就是实实在在物理内存上的地址,你PC上有1G内存,那最大地址就是0x40000000,0x800就是代表1KB的地址。
线性地址:这是APP用的地址,也就是我们程序员写代码用的地址,它是一个虚拟地址,最终会被转化到物理地址。
逻辑地址:这是最麻烦的一个地址了,它是针对x86的架构形成的地址,先不用理会。
问题背景:早起x86的CPU内部有20根地址线,能寻址2^20个地址,也就是1MB。但是寄存器只有16位,只能寻址2^16个地址,也就是64KB。怎么利用寄存器的16位来寻址1MB呢?
解决方案:intel想一个办法,寻址分为两部分:基地址和偏移地址。首先CPU要到CS(代码段寄存器)中取出基地址,然后再到IP寄存器中取出指令偏移量,组合成一个20位地址(这就是线性地址)然后寻址。但是还是不够,寄存器都是16位的,那么相加能表示的最大地址就是 0xffff+0xffff=128KB。为了解决这个问题,cs寄存器中的数字不再代表实际基地址,而是基地址的索引。1MB的内存分成29个64KB,cs寄存器中存放1-29这30个数字,线性地址 = cs*64KB + IP。
逻辑地址再说明一下:
逻辑地址的选择符就对应上面讲的CS寄存器,偏移量对应IP寄存器。32位操作系统嫌32的寄存器能表达的信息太少,不用上面讲的直接得出线性地址。而是再做一次转换,先把段基址存到一块内存,每个段基址占8字节(64位),当然这8字节里面还含有其他描述信息,描述符会把index字段*8得到某个段的描述符的地址,然后跑到那个地址取出里边的段基址再加上IP寄存器的偏移量就得到线性地址了。
总结:段式管理可以解决短位寄存器访问大内存的问题。但是如果这样20位的CPU系统只能支持最大的内存为64KB,这样当然不可取。下面再介绍分页管理机制。
2、页式管理机制(linux下内核开发管理内存的方式)
页目录、页表:页式管理的两个线性表,用来查找存线性地址的页
页框:对应真正的物理地址,以4KB为边界。
一个32位的线性地址会被分成3个部分,10位页目录,10位页表,12位页偏移。理解为内存中存储的一张张的拥有1024个表项的二维表,页目录每一项存放的是某个页表的地址,而页表中每一项存放的是某个页的地址。可以想象为C语言中指针。
举个例子:
线性地址是 0x00401001 ,表示为二进制是 0000000001 0000000001 000000000001 ,对应上面三个名词,那么页目录值、页表值和页内偏移值都是1,也就说要想找到真正的物理地址,首先应该到页目录(拥有1024个页目录项)的第一项中寻址,这个项中存的是页表的地址,假如是1000,CPU就会跑到1000的物理地址(注意,真的是物理地址)上找到某个页表,这个页表又拥有1024项的页表项。然后拿到第二个10位地址(还是1),也就说到第一个页表上的第一项,到这里,其实就已经能找到物理内存块了。想一下,每个页表项代表4KB,每个页表又1024个页表项,而页目录项总共有1024个页目录项(=1024张页表),所以能表示的总大小正好是4kb*1024*1024=4GB。要定位到具体的字节就用到最后的12位了,再加上12位的偏移值即可。
3、DPDK 使用大页
上面介绍的页式管理机制,引入TLB概念(转换后备缓冲器 translation lookaside buffer),TLB中保存着逻辑地址前20位(页目录、页表)和页号(物理地址)的对应关系,如果匹配到逻辑地址就可以迅速的找到页号,通过页号与逻辑地址的后12位的偏移组合得到最终的物理地址。
所以,如果一个程序使用了2MB的内存,那么需要512个页表表项(512*4KB=2048KB=2MB)才能保证不会出现TLB不命中的情况。由于TLB资源是有限的,随着内存使用的增加,势必增加TLB不命中的情况。自然会想到,如果页框的值为2M,只需要一个TLB。由于DPDK性能的要求和内存的大量使用,自然引入大页的概念。
Linux操作系统采用了基于hugetlbfs 的2MB或者1GB的大页面的支持。修改内核参数即可预留大页。
非NUMA系统中,预留1024个大小为2M的大页
echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kb/nr_hugepages
NUMA系统中,两个NODE的系统中,预留1024个2M的大页
echo 1024 > /sys/devices/system/node/node0/hugepages/hugepages-2048kb/nr_hugepages
echo 1024 > /sys/devices/system/node/node1/hugepages/hugepages-2048kb/nr_hugepages
大页预留了,下面讲到使用的问题。DPDK使用HUGETLBFS来使用大页。首先,它需要把大页mount到某个路径 比如 /mnt/huge。
mkdir /mnt/huge
mount -t hugetlbfs nodev /mnt/huge
可以开机时自动挂载:
nodev /mnt/huge hugetlbfs defaults 0 0
接下来,DPDK运行的时候,会使用mmap()系统调用把大页映射到用户态的虚拟地址空间(在堆和栈之间),然后就可以正常使用了。
ptr=mmap(NULL, len , PROT_READ|PROT_WRITE, MAP_SHARED , fd , 0);第一个参数为映射的起始地址,填NULL表示由内核分配起始地址。如果填写指定的地址,该指定的地址需要在LD链接的时候预留好。LD详细信息为 ld -verbose
4、DPDK内存管理分析