内存管理
内存管理
物理内存
struct pglist_data:在NUMA中内存访问速度一致的部分称为一个节点
struct zone:ZONE_DMA:0-16MB的页框,直接映射到内核空间;
ZONE_NORMAL:16-896MB之间的内存页框,常规页框,直接映射到内核的地址空间;
ZONE_HIGHMEM:896MB-4GB的物理内存(被内核空间的高端内存线性地址空间(896-1024MB)动态映射)
struct page:内核初始化构建一个大小为页框数的mem_map[]数组与物理页框一一对应
伙伴分配算法:所有的空闲页框分组为11个块链表,每个块链表分别包含大小为1,2,4,8,16,32,64,128,256,512和1024个连续页框的页框块
slab分配器:通过将内存按使用对象不同,再将伙伴分配的页框划分成不同大小的空间,应用于内核对象的缓存
虚拟内存
mm_struct:描述一个进程整个虚拟地址空间;包含多个虚拟区间,虚拟区间少时用链表,多时用红黑树
vm_area_struct:进程分为代码段、数据段、BSS、堆、栈;内核将这些区间抽象成vm_area_struct的对象来管理;vm_operations_struct描述这个区间的操作方式
malloc:是用户空间申请内存的方法,分配的地址是虚拟地址连续,物理地址一般不会连续,通过系统调用sys_brk来实现,sys_brk分配的地址是经过TLB的,sys_brk分配的仅仅是一个虚拟的空间vma,在分配时并没有做实际物理页的分配动作,实际分配物理页的动作是在do_page_fault异常处理中完成的
vmalloc:分配动态内存映射区内存,虚拟地址连续,物理地址可不连续;vmalloc分配内存时不仅仅分配了虚拟空间vma,并且还分配了实际的物理页,不过这个物理页是和在公共的页表进行了映射,并没有在本进程的页表进行映射.所以当访问这段vma时,触发的do_page_fault异常处理在实际上完成了页表的同步工作
kmalloc:基于slab分配常规映射的内核空间,虚拟地址和物理地址都连续,常用于小块内存分配;这部分也有页表,只不过映射关系早被内核做好了;因为用户空间的不同进程的内存地址可能相同,所以映射关系不能固定,只能一个进程一个映射关系(页表)
Cache一致性
多级cache
一般有如下程序优化方法:优化数据缓存(数据访问顺序);指令缓存(cpu分支预测器,likely/unlikely);绑核提高缓存命中率
Cache line
Cache是由cache Line组成 它是 CPU 从内存读取数据的基本单位,而cache Line 是由各种标志(Tag)+ 数据块(Data Block)组成;但是怎么写呢?
写直达:如果命中,直接将数据写到cache和主存;如果没命中,直接写到主存
还有写回:写到Cache line,标记为脏;当修改过的 Cache line被替换(替换算法决定时机)时,才需要写到内存中
一致性问题
如果按照写回方法:
当a=0;被核心1写a+1;此时a=1还放在cache line中,在这个cache line还没被替换时,主存中a还是=0;此时,核心2正好从主存中读取了a,把a放到了核心2的cache line;结果两个核心的cache里的a不一致,产生了一致性问题;为解决这个问题,引出了“写传播”和“事务串行化”
总线嗅探
当核心1里面修改了cache某个数据时,通过总线广播给其他核心,每个核心也会监听广播的事件,如果自己的cache也有相同的数据,则会更新到自己的L1 cache;这种方式只保证了写传播,没保证事务串行化,于是有了mesi:基于总线嗅探实现事务串行化
mesi协议
用四种状态来标记cache line的状态M:已修改;E:独占;S:共享;I:失效;用状态机来减少广播和实现串行化
Dma跟cache一致性
dma外设到内存:需要将数据对应的cache line设为i,cpu再使用数据就重新从内存拿
dma内存到外设:如果cache line为m,需要将cache的数据同步到内存
屏障指令
Mesi虽然保证了cache一致性,但是没有解决执行乱序的问题,因为执行乱序是一定存在的,在不能的乱序的地方需要使用屏障指令(保证指令跟数据按照程序逻辑执行)避免它,比如互斥锁里面就用到了屏障指令
a=0;a=1,flag=1;//核心1执行,flag先于a=1执行
if(flag==1) b=a+1;//核心2执行,b的结果跟程序的逻辑就不一致了
单核:有依赖点,结果不会出错
多核:flag先命中cache,先执行,结果就会出错