Linux内核内存管理之处理器缓存(从这五个点入手~)
全网最牛Linux内核分析--Intel CPU体系结构
一文让你读懂Linux五大模块内核源码,内核整体架构设计(超详细)
嵌入式前景真的好吗?那有点悬!
一文教你如何使用GDB+Qemu调试Li
好文推荐: 全网最牛Linux内核分析--Intel CPU体系结构 一文让你读懂Linux五大模块内核源码,内核整体架构设计(超详细) 嵌入式前景真的好吗?那有点悬! 一文教你如何使用GDB+Qemu调试Linux内核 Linux内核必读五本书籍(强烈推荐) 全网独一无二Linux内核Makefle系统文件详解(一)(纯文字代码) 带你深度了解Linux内核架构和工作原理! 1 处理器缓存缓存是静态随机访问存储器(Static Random Access Memory,SRAM),访问速度接近于处理器的速度,但是集成度低,和内存相比,在容量相同的情况下体积大,并且价格昂贵。内存是动态随机访问存储器(Dynamic Random Access Memory,DRAM),访问速度慢,但是集成度高,和缓存相比,在容量相同的情况下体积小。1.1 缓存结构 把从物理地址生成索引和标签的缓存称为物理索引物理标签(Physically Indexed Physically Tagged,PIPT)缓存。把从虚拟地址生成索引、从物理地址生成标签的缓存称为虚拟索引物理标签(Virtually Indexed Physically Tagged,VIPT)缓存。 PIPT缓存。VPIPT(VMID-aware PIPT)缓存,即感知虚拟机标识符的PIPT缓存。VIPT缓存。 【文章福利】小编推荐自己的Linux内核技术交流群:【891587639】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!前100名进群领取,额外赠送大厂面试题。 学习直通车: 内核资料直通车: 1.2 缓存策略 1.写分配(write allocation)。 如果处理器写数据的时候没有命中缓存行,那么分配一个缓存行,然后读取数据并填充缓存行,接着把数据写到缓存行。 2.读分配(read allocation)。 如果处理器读数据的时候没有命中缓存行,那么分配一个缓存行。 1.写回(write-back)。 处理器写数据的时候,只更新缓存,把缓存行标记为脏。只在缓存行被替换或被程序清理的时候更新内存。 2.写透(write-through)。 处理器写数据的时候,同时更新缓存和内存,但不会把缓存行标记为脏。 PRFM , | labelPLD表示加载预取(prefetch for load),PST表示存储预取(prefetch for store)。L1表示1级缓存,L2表示2级缓存,L3表示3级缓存。KEEP表示把预取的数据保存在缓存中(因为预取的数据会被使用多次),称为保存预取(retain prefetch)或暂存预取(temporal prefetch);STRM(stream)表示直接把预取的数据传给处理器的核(因为预取的数据只会使用一次,不需要保存在缓存中),称为流动预取(streaming prefetch)或非暂存预取(non-temporal prefetch)。1.3 缓存维护内核修改或删除页表项的时候,需要冲刷缓存。内核使用内核虚拟地址修改进程的物理页,为了避免产生内核虚拟地址和用户虚拟地址之间的缓存别名问题,需要冲刷缓存。和外围设备交互时,处理器写数据到DMA区域的内存块,然后通过设置外围设备的控制器上的控制寄存器发送命令,外围设备通过DMA控制器从物理内存读取数据。DMA控制器读取数据时不经过处理器的缓存,所以处理器写完数据以后必须冲刷缓存,把缓存中的数据写回到物理内存。如果缓存行的有效位被设置,且脏位被设置,即缓存行包含数据并且被修改过,那么把数据写回到物理内存,然后清除有效位。如果缓存行的有效位被设置,但脏位没有被设置,即缓存行包含数据并且没有被修改过,那么只需要清除有效位。1.内核修改页表
2.内核修改进程的物理页 1.void copy_user_page(void *to, void *from, unsigned long addr, struct page *page) 父进程分叉生成子进程,子进程和父进程共享物理页。当其中一个进程试图写私有的匿名页时,触发页错误异常,执行写时复制,分配新的物理页,使用函数copy_user_page把旧的物理页里面的数据复制到新的物理页,内核使用函数kmap_atomic临时把进程的物理页映射到内核虚拟地址空间。 2.void clear_user_page(void *to, unsigned long addr, struct page *page) 把进程的物理页清零。内核使用函数kmap_atomic临时把进程的物理页映射到内核虚拟地址空间。 3.void copy_to_user_page(struct vm_area_struct *vma, struct page *page, unsigned long user_vaddr, void *dst, void *src, int len) 内核复制数据到用户页,内核使用kmap临时把用户页映射到内核虚拟地址空间。 3.ARM64处理器的缓存维护使缓存行失效(invalidate):清除缓存行的有效位。清理(clean)缓存行:首先把标记为脏的缓存行里面的数据写到下一级缓存或内存,然后清除缓存行的有效位。只适用于使用写回策略的数据缓存。清零(zero):把缓存里面的一个内存块清零,不需要先从内存读数据到缓存中。只适用于数据缓存。
一致点(Point of Coherency,PoC):保证所有可能访问内存的观察者(例如处理器核和DMA控制器)看到某一内存位置的相同副本的点,通常是物理内存。统一点(Point of Unification,PoU):一个核的统一点是保证这个核的指令缓存、数据缓存和TLB看到某内存位置的相同副本的点。例如,如果一个处理器核有一级指令缓存、一级数据缓存和TLB,那么这个处理器核的统一点是统一二级缓存。1.4 SMP缓存一致性修改(Modified):表示数据只在本处理器的缓存中存在副本,数据是脏的,即数据被修改过,没有写回到内存。独占(Exclusive):表示数据只在本处理器的缓存中存在副本,数据是干净的,即副本和内存中的数据相同。共享(Shared):表示数据可能在多个处理器的缓存中存在副本,数据是干净的,即所有副本和内存中的数据相同。无效(Invalid):表示缓存行中没有存放数据。读(Read):包含想要读取的缓存行的物理地址。读响应(Read Response):包含读消息请求的数据。读响应消息可能是由内存控制器发送的,也可能是由其他处理器的缓存发送的。如果一个处理器的缓存有想要的数据,并且处于修改状态,那么必须发送读响应消息。使无效(Invalidate):包含想要删除的缓存行的物理地址。所有其他处理器必须从缓存中删除对应的数据,并且发送使无效确认消息来应答。使无效确认(Invalidate Acknowledge):处理器收到使无效消息,必须从缓存中删除对应的数据,并且发送使无效确认消息来应答。读并且使无效(Read Invalidate):包含想要读取的缓存行的物理地址,同时要求从其他缓存中删除数据。它是读消息和使无效消息的组合,需要接收者发送读响应消息和使无效确认消息。写回(Writeback):包含想要写回到内存的地址和数据。 转换a,修改到独占:处理器收到写回消息,把缓存行写回内存,但是缓存行保留数据。转换b,独占到修改:处理器写数据到缓存行。转换c,修改到无效:处理器收到“读并且使无效”消息,发送读响应消息和使无效确认消息,删除本地副本(不需要写回内存,因为发送“读并且使无效”消息的处理器需要写数据)。转换d,无效到修改:处理器存储不在本地缓存中的数据,发送“读并且使无效”消息,通过读响应消息收到数据。处理器可以在收到所有其他处理器的使无效确认消息以后转换到修改状态。转换e,共享到修改:处理器存储数据,发送使无效消息,收到所有其他处理器的使无效确认消息以后转换到修改状态。转换f,修改到共享:其他处理器读取缓存行,发送读消息,本处理器收到读消息后,写回内存,保留一个只读副本,发送读响应消息。转换g,独占到共享:其他处理器读取缓存行,发送读消息,本处理器收到后发送读响应消息,保留一个只读副本。转换h,共享到独占:本处理器意识到很快需要写数据,发送使无效消息,收到所有其他处理器的使无效确认消息以后转换到独占状态。转换i,独占到无效:其他处理器存储数据,发送“读并且使无效”消息,本处理器收到消息后,发送读响应消息和使无效确认消息。 转换j,无效到独占:处理器存储不在本地缓存中的数据,发送“读并且使无效”消息,收到读响应消息和所有其他处理器的使无效确认消息后转换到独占状态,完成存储操作后转换到修改状态。转换k,无效到共享:处理器加载不在本地缓存中的数据,发送读消息,收到读响应消息后转换到共享状态。转换l,共享到无效:其他处理器存储本地缓存中的数据,发送使无效消息,本处理器收到后,把缓存行的状态转换为无效,发送使无效确认消息。处理器0加载地址n的数据,因为本地缓存没有副本,所以发送读消息。内存控制器从内存读取数据后发送读响应消息。处理器0收到读响应消息后,缓存行从无效状态转换到共享状态。处理器1加载地址n的数据,因为本地缓存没有副本,所以发送读消息。处理器0收到读消息后,缓存行保持共享状态不变,发送读响应消息。处理器1收到读响应消息后,把缓存行从无效状态转换到共享状态。处理器0存储地址n的数据,因为缓存行处于共享状态,所以发送使无效消息。处理器1收到使无效消息后,把缓存行从共享状态转换到无效状态,发送使无效确认消息。处理器0收到使无效确认消息后,把缓存行从共享状态转换到修改状态。下面分两种情况:1)如果处理器1加载地址n的数据,因为本地缓存没有副本,所以发送读消息。处理器0收到读消息后,写回内存,从修改状态转换到共享状态,发送读响应消息。处理器1收到读响应消息后,从无效状态转换到共享状态。 2)如果处理器1存储地址n的数据,因为本地缓存没有副本,所以发送“读并且使无效”消息。处理器0收到“读并且使无效”消息后,发送读响应消息和使无效确认消息,从修改状态转换到无效状态。处理器1收到读响应消息和使无效确认消息后,修改数据,从无效状态转换到修改状态。1.存储缓冲区 第一个问题是:假设变量a的值是0,处理器0的缓存中没有变量a的副本,处理器1的缓存中有变量a的副本。处理器0把变量a设置为1,在自己的缓存中没有找到变量a,发送“读并且使无效”消息,把变量a的值写到存储缓冲区中,然后继续执行指令,后面有一条加载指令加载变量a。处理器0收到处理器1的读响应消息,变量a的值是0。处理器0在缓存中找到变量a,值为0,但是变量a的最新值是1,在存储缓冲区中。 解决办法是:处理器加载数据时,必须首先在自己的存储缓冲区中查找数据,即把存储直接转发给随后的加载操作,称为“存储转发(store forwarding)”,如下所示:
把存储缓冲区的所有数据冲刷到缓存中。写内存屏障为当前存储缓冲区中的所有数据做标记,如果存储缓冲区中有标记过的数据,那么后面的存储指令只能把数据写到存储缓冲区中,不能写到缓存中。
2.使无效队列
处理器0执行“a=1”,因为缓存行处于共享状态,所以处理器0把a的新值放在存储缓冲区中,发送使无效消息。处理器1收到使无效消息,存放到使无效队列中,立即发送使无效确认消息。当处理器1执行“assert(a == 1)”的时候,因为a的旧值还在处理器1的缓存中,所以断言失败。处理器1处理使无效队列中的消息,使包含a的缓存行无效。
变量a的最新值在处理器0的存储缓冲区中,处理器0需要执行写内存屏障。处理器1的使无效队列包含使包含变量a的缓存行无效的消息,处理器1需要执行读内存屏障。1.5 利用缓存提高性能的编程技巧使变量的起始地址对齐到一级缓存行长度的整数倍。使结构体对齐到一级缓存行长度的整数倍,实现的效果是:结构体的所有变量的起始地址是一级缓存行长度的整数倍,并且结构体的长度是一级缓存行长度的整数倍。使结构体中一个字段的偏移对齐到一级缓存行长度的整数倍。 1.以4个下划线开头的宏____cacheline_aligned:对齐到一级缓存行的长度。
2.以4个下划线开头的宏____cacheline_aligned_in_smp:在对称多处理器系统中等价于宏____cacheline_aligned,在单处理器系统中是空的宏。
3.以两个下划线开头的宏__cacheline_aligned:对齐到一级缓存行的长度,并且把变量放在“.data…cacheline_aligned”节中。
4.以两个下划线开头的宏__cacheline_aligned_in_smp:在对称多处理器系统中等价于宏__cacheline_aligned,在单处理器系统中是空的宏。
结构体netdev_queue对齐到一级缓存行长度的整数倍linux处理器,该结构体的所有变量的起始地址是一级缓存行长度的整数倍,并且该结构体的长度是一级缓存行长度的整数倍。把只读字段集中放在一起,把可写字段集中放在一起,把第一个可写字段的偏移对齐到一级缓存行长度的整数倍,让只读字段和可写字段处于不同的缓存行中,避免可写字段影响只读字段:
(编辑:威海站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |